blob: 3d7d23f3d291579b2f02213f8395c3584400455a [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 Yuca51ee22013-06-27 21:45:24 +0800137static int __dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info, uint32_t target_variant_id);
138
Joel Kingaa335dc2013-06-03 16:11:08 -0700139/* Function to obtain the index information for the correct device tree
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700140 * based on the platform data.
Joel Kingaa335dc2013-06-03 16:11:08 -0700141 * If a matching device tree is found, the information is returned in the
142 * "dt_entry_info" out parameter and a function value of 0 is returned, otherwise
143 * a non-zero function value is returned.
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700144 */
Joel Kingaa335dc2013-06-03 16:11:08 -0700145int dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700146{
Maria Yuca51ee22013-06-27 21:45:24 +0800147 uint32_t target_variant_id;
148
149 if(board_hardware_id() == HW_PLATFORM_QRD) {
150 target_variant_id = board_target_id();
151 if (__dev_tree_get_entry_info(table, dt_entry_info, target_variant_id) == 0) {
152 return 0;
153 }
154 }
155 /*
156 * for compatible with version 1 and version 2 dtbtool
157 * will compare the subtype inside the variant id
158 */
159 target_variant_id = board_hardware_id() | ((board_hardware_subtype() & 0xff) << 24);
160
161 return __dev_tree_get_entry_info(table, dt_entry_info, target_variant_id);
162}
163
164static int __dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info, uint32_t target_variant_id)
165{
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700166 uint32_t i;
Joel Kingaa335dc2013-06-03 16:11:08 -0700167 unsigned char *table_ptr;
168 struct dt_entry dt_entry_buf_1;
169 struct dt_entry dt_entry_buf_2;
170 struct dt_entry *cur_dt_entry;
171 struct dt_entry *best_match_dt_entry;
172 struct dt_entry_v1 *dt_entry_v1;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700173
Joel Kingaa335dc2013-06-03 16:11:08 -0700174 if (!dt_entry_info) {
175 dprintf(CRITICAL, "ERROR: Bad parameter passed to %s \n",
176 __func__);
177 return -1;
178 }
179
180 table_ptr = (unsigned char *)table + DEV_TREE_HEADER_SIZE;
181 cur_dt_entry = &dt_entry_buf_1;
182 best_match_dt_entry = NULL;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700183
184 for(i = 0; i < table->num_entries; i++)
185 {
Joel Kingaa335dc2013-06-03 16:11:08 -0700186 memset(cur_dt_entry, 0, sizeof(struct dt_entry));
187 switch(table->version) {
188 case DEV_TREE_VERSION_V1:
189 dt_entry_v1 = (struct dt_entry_v1 *)table_ptr;
190 cur_dt_entry->platform_id = dt_entry_v1->platform_id;
191 cur_dt_entry->variant_id = dt_entry_v1->variant_id;
192 cur_dt_entry->soc_rev = dt_entry_v1->soc_rev;
193 cur_dt_entry->board_hw_subtype = board_hardware_subtype();
194 cur_dt_entry->offset = dt_entry_v1->offset;
195 cur_dt_entry->size = dt_entry_v1->size;
196 table_ptr += sizeof(struct dt_entry_v1);
197 break;
198 case DEV_TREE_VERSION_V2:
199 memcpy(cur_dt_entry, (struct dt_entry *)table_ptr,
200 sizeof(struct dt_entry));
201 table_ptr += sizeof(struct dt_entry);
202 break;
203 default:
204 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
205 table->version);
206 return -1;
207 }
208
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800209 /* DTBs are stored in the ascending order of soc revision.
210 * For eg: Rev0..Rev1..Rev2 & so on.
211 * we pickup the DTB with highest soc rev number which is less
212 * than or equal to actual hardware
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700213 */
Joel Kingaa335dc2013-06-03 16:11:08 -0700214 if((cur_dt_entry->platform_id == board_platform_id()) &&
Maria Yuca51ee22013-06-27 21:45:24 +0800215 ((cur_dt_entry->variant_id | ((cur_dt_entry->board_hw_subtype & 0xff) << 24)) == target_variant_id))
Joel Kingaa335dc2013-06-03 16:11:08 -0700216 {
217 if(cur_dt_entry->soc_rev == board_soc_version()) {
David Ng618293a2013-06-25 12:29:03 -0700218 best_match_dt_entry = cur_dt_entry;
219 break;
Joel Kingaa335dc2013-06-03 16:11:08 -0700220 } else if (cur_dt_entry->soc_rev < board_soc_version()){
221 /* Keep this as the next best candidate. */
222 if (!best_match_dt_entry) {
223 best_match_dt_entry = cur_dt_entry;
224 cur_dt_entry = &dt_entry_buf_2;
225 } else {
226 /* Swap dt_entry buffers */
227 struct dt_entry *temp = cur_dt_entry;
228 cur_dt_entry = best_match_dt_entry;
229 best_match_dt_entry = temp;
230 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700231 }
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800232 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700233 }
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800234
Joel Kingaa335dc2013-06-03 16:11:08 -0700235 if (best_match_dt_entry) {
236 *dt_entry_info = *best_match_dt_entry;
David Ng618293a2013-06-25 12:29:03 -0700237 dprintf(INFO, "Using DTB entry %u/%08x/%u/%u for device %u/%08x/%u/%u\n",
238 dt_entry_info->platform_id, dt_entry_info->soc_rev,
239 dt_entry_info->variant_id, dt_entry_info->board_hw_subtype,
240 board_platform_id(), board_soc_version(),
241 board_hardware_id(), board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700242 return 0;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800243 }
244
David Ng618293a2013-06-25 12:29:03 -0700245 dprintf(CRITICAL, "ERROR: Unable to find suitable device tree for device (%u/0x%08x/%u/%u)\n",
246 board_platform_id(), board_soc_version(),
247 board_hardware_id(), board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700248 return -1;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700249}
250
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700251/* Function to add the first RAM partition info to the device tree.
252 * Note: The function replaces the reg property in the "/memory" node
253 * with the addr and size provided.
254 */
255int dev_tree_add_first_mem_info(uint32_t *fdt, uint32_t offset, uint32_t addr, uint32_t size)
256{
257 int ret;
258
259 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
260
261 if (ret)
262 {
263 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
264 ret);
265 }
266
267
268 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
269
270 if (ret)
271 {
272 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
273 ret);
274 }
275
276 return ret;
277}
278
279/* Function to add the subsequent RAM partition info to the device tree. */
280int dev_tree_add_mem_info(void *fdt, uint32_t offset, uint32_t addr, uint32_t size)
281{
282 static int mem_info_cnt = 0;
283 int ret;
284
285 if (!mem_info_cnt)
286 {
287 /* Replace any other reg prop in the memory node. */
288 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
289 mem_info_cnt = 1;
290 }
291 else
292 {
293 /* Append the mem info to the reg prop for subsequent nodes. */
294 ret = fdt_appendprop_u32(fdt, offset, "reg", addr);
295 }
296
297 if (ret)
298 {
299 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
300 ret);
301 }
302
303
304 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
305
306 if (ret)
307 {
308 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
309 ret);
310 }
311
312 return ret;
313}
314
315/* Top level function that updates the device tree. */
316int update_device_tree(void *fdt, const char *cmdline,
317 void *ramdisk, uint32_t ramdisk_size)
318{
319 int ret = 0;
320 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700321
322 /* Check the device tree header */
323 ret = fdt_check_header(fdt);
324 if (ret)
325 {
326 dprintf(CRITICAL, "Invalid device tree header \n");
327 return ret;
328 }
329
Deepa Dinamani1c970732013-04-19 14:23:01 -0700330 /* Add padding to make space for new nodes and properties. */
331 ret = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + DTB_PAD_SIZE);
332 if (ret!= 0)
333 {
334 dprintf(CRITICAL, "Failed to move/resize dtb buffer: %d\n", ret);
335 return ret;
336 }
337
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700338 /* Get offset of the memory node */
339 ret = fdt_path_offset(fdt, "/memory");
340 if (ret < 0)
341 {
342 dprintf(CRITICAL, "Could not find memory node.\n");
343 return ret;
344 }
345
346 offset = ret;
347
348 ret = target_dev_tree_mem(fdt, offset);
349 if(ret)
350 {
351 dprintf(CRITICAL, "ERROR: Cannot update memory node\n");
352 return ret;
353 }
354
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700355 /* Get offset of the chosen node */
356 ret = fdt_path_offset(fdt, "/chosen");
357 if (ret < 0)
358 {
359 dprintf(CRITICAL, "Could not find chosen node.\n");
360 return ret;
361 }
362
363 offset = ret;
364 /* Adding the cmdline to the chosen node */
Amol Jadi10c7d1c2013-01-25 13:24:29 -0800365 ret = fdt_setprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700366 if (ret)
367 {
368 dprintf(CRITICAL, "ERROR: Cannot update chosen node [bootargs]\n");
369 return ret;
370 }
371
372 /* Adding the initrd-start to the chosen node */
373 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-start", (uint32_t)ramdisk);
374 if (ret)
375 {
376 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-start]\n");
377 return ret;
378 }
379
380 /* Adding the initrd-end to the chosen node */
381 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-end", ((uint32_t)ramdisk + ramdisk_size));
382 if (ret)
383 {
384 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-end]\n");
385 return ret;
386 }
387
388 fdt_pack(fdt);
389
390 return ret;
391}
Maria Yuca51ee22013-06-27 21:45:24 +0800392