blob: 6a06a12012ce4f07a4eaf54a83c765cb4459f0f5 [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
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070039extern int target_is_emmc_boot(void);
40extern uint32_t target_dev_tree_mem(void *fdt, uint32_t memory_node_offset);
41
Channagoud Kadabi11682e92013-02-28 11:21:46 -080042/*
43 * Argument: Start address of the kernel loaded in RAM
44 * Return Value: DTB address : If appended device tree is found
45 * '0' : Otherwise
46 */
47uint32_t dev_tree_appended(void *kernel)
48{
49 uint32_t app_dtb_offset = 0;
50 uint32_t dtb_magic = 0;
51 uint32_t dtb = 0;
52
53 memcpy((void*) &app_dtb_offset, (void*) (kernel + DTB_OFFSET), sizeof(uint32_t));
54 memcpy((void*) &dtb_magic, (void*) (kernel + app_dtb_offset), sizeof(uint32_t));
55
56 if (dtb_magic == DTB_MAGIC) {
57 dprintf(INFO, "Found Appeneded Flattened Device tree\n");
58 dtb = (uint32_t) (kernel + app_dtb_offset);
59 }
60
61 return dtb;
62}
63
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070064/* Function to return the pointer to the start of the correct device tree
65 * based on the platform data.
66 */
67struct dt_entry * dev_tree_get_entry_ptr(struct dt_table *table)
68{
69 uint32_t i;
70 struct dt_entry *dt_entry_ptr;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -080071 struct dt_entry *latest_dt_entry = NULL;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070072
73 dt_entry_ptr = (struct dt_entry *)((char *)table + DEV_TREE_HEADER_SIZE);
74
75 for(i = 0; i < table->num_entries; i++)
76 {
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -080077 /* DTBs are stored in the ascending order of soc revision.
78 * For eg: Rev0..Rev1..Rev2 & so on.
79 * we pickup the DTB with highest soc rev number which is less
80 * than or equal to actual hardware
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070081 */
82 if((dt_entry_ptr->platform_id == board_platform_id()) &&
83 (dt_entry_ptr->variant_id == board_hardware_id()) &&
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -080084 (dt_entry_ptr->soc_rev == board_soc_version()))
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070085 {
86 return dt_entry_ptr;
87 }
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -080088 /* if the exact match not found, return the closest match
89 * assuming it to be the nearest soc version
90 */
91 if((dt_entry_ptr->platform_id == board_platform_id()) &&
92 (dt_entry_ptr->variant_id == board_hardware_id()) &&
93 (dt_entry_ptr->soc_rev <= board_soc_version())) {
94 latest_dt_entry = dt_entry_ptr;
95 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070096 dt_entry_ptr++;
97 }
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -080098
99 if (latest_dt_entry) {
100 dprintf(SPEW, "Loading DTB with SOC version:%x\n", latest_dt_entry->soc_rev);
101 return latest_dt_entry;
102 }
103
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700104 return NULL;
105}
106
107/* Function to return the offset of flash node - "/qcom,mtd-partitions".
108 * The function creates this node if it does not exist.
109 */
110static uint32_t dev_tree_get_flash_node_offset(void *fdt)
111{
112 uint32_t offset;
113 int ret = DT_OP_SUCCESS;
114
115 /* Find the mtd node. */
116 ret = fdt_path_offset(fdt, "/qcom,mtd-partitions");
117
118 if (ret & FDT_ERR_NOTFOUND)
119 {
120 /* Node not found.
121 * Add node as sub node of root.
122 */
123 ret = fdt_path_offset(fdt, "/");
124
125 if (ret < 0)
126 {
127 dprintf(CRITICAL, "Unable to calculate root offset\n");
128 ret = DT_OP_FAILURE;
129 goto dev_tree_get_flash_node_offset_err;
130 }
131
132 offset = ret;
133 /* Add flash partition node. */
134 ret = fdt_add_subnode(fdt, offset, "qcom,mtd-partitions");
135
136 if (ret < 0)
137 {
138 dprintf(CRITICAL, "Unable to add partition node. \n");
139 ret = DT_OP_FAILURE;
140 goto dev_tree_get_flash_node_offset_err;
141 }
142
143 /* Save the offset of the added node. */
144 offset = fdt_path_offset(fdt, "/qcom,mtd-partitions");
145 }
146 else if (ret != 0)
147 {
148 dprintf(CRITICAL, "Unable to calculate partition node offset\n");
149 ret = DT_OP_FAILURE;
150 goto dev_tree_get_flash_node_offset_err;
151 }
152
153dev_tree_get_flash_node_offset_err:
154
155 return offset;
156}
157
158/* Function to add the individual nand partition nodes to the device tree.
159 * Pre-condition: The function assumes the presence of
160 * "/qcom,mtd-partitions" device node.
161 */
162static int dev_tree_add_ptable_nodes(void *fdt, uint32_t parent_offset)
163{
164 struct ptable *ptable;
165 int n;
166 unsigned char array[100];
167 unsigned char *ptn_name_array;
168 int dt_ret = DT_OP_SUCCESS;
169 int ret;
170 int i;
171 uint32_t node_offset;
172 uint32_t blk_size;
173
174 n = sizeof("partition@");
175
176 /* Allocate bytes to save partition name:
177 * Since address is of uint uint32_t,
178 * allocate twice this size for string
179 * as 1 digit occupies 1 byte is ASCII.
180 */
181 ptn_name_array = (unsigned char*) malloc(sizeof(uint32_t) * 2 + n + 1);
182
183 if (ptn_name_array == NULL)
184 {
185 dprintf(CRITICAL, "Failed to allocate memory for flash partition name\n");
186 return -1;
187 }
188
189 strcpy((char*)ptn_name_array, (const char*)"partition@");
190
191 /* Add ptable nodes. */
192 ptable = flash_get_ptable();
193 /* Get block size. */
194 blk_size = flash_block_size();
195
Deepa Dinamani52aca8d2013-02-05 11:41:41 -0800196 dprintf(INFO, "Add %d flash partitions to dt: start\n", ptable->count);
197
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700198 /* Need to add partitions in reverse order since libfdt adds
199 * new nodes on the top.
200 * Kernel looks to mount the partitions in the order specified in
201 * the partition.xml in the meta build.
202 */
203 for (i = (ptable->count - 1); i >= 0; i--)
204 {
205 /* Add the partition node. */
206 if (itoa(ptable->parts[i].start * blk_size, ptn_name_array + n - 1, sizeof(uint32_t) * 2 + 1, 16) < 0)
207 {
208 dprintf(CRITICAL, "String len exceeded for itoa\n");
209 return -1;
210 }
211
212 strcpy((char *)array, (const char*)"/qcom,mtd-partitions/");
213 strcat((char*)array, (const char*)ptn_name_array);
214
215 ret = fdt_add_subnode(fdt, parent_offset, (const char*)ptn_name_array);
216
217 if (ret < 0)
218 {
219 dprintf(CRITICAL, "Unable to add partition node: %s.\n",
220 ptn_name_array);
221 dt_ret = DT_OP_FAILURE;
222 goto dev_tree_add_ptable_nodes_err;
223 }
224
225 dt_ret = fdt_path_offset(fdt, (const char*)array);
226
227 if (dt_ret < 0)
228 {
229 dprintf(CRITICAL, "Unable to calculate parition node offset: %s\n",
230 ptn_name_array);
231 dt_ret = DT_OP_FAILURE;
232 goto dev_tree_add_ptable_nodes_err;
233 }
234
235 node_offset = dt_ret;
236
237 /* Add the partition name as label. */
238 ret = fdt_setprop_string(fdt, node_offset, (const char*)"label", (const char*)ptable->parts[i].name);
239
240 if (ret != 0)
241 {
242 dprintf(CRITICAL, "Unable to add label property: %s.\n",
243 ptable->parts[i].name);
244 dt_ret = DT_OP_FAILURE;
245 goto dev_tree_add_ptable_nodes_err;
246 }
247
248 /* Add the reg values. */
249 ret = fdt_setprop_u32(fdt, node_offset, (const char*)"reg", ptable->parts[i].start * blk_size);
250
251 if (ret != 0)
252 {
253 dprintf(CRITICAL, "Unable to add reg property. %s\n",
254 ptable->parts[i].name);
255 dt_ret = DT_OP_FAILURE;
256 goto dev_tree_add_ptable_nodes_err;
257 }
258
259 ret = fdt_appendprop_u32(fdt, node_offset, (const char*)"reg", ptable->parts[i].length * blk_size);
260
261 if (ret != 0)
262 {
263 dprintf(CRITICAL, "Unable to add reg property. %s\n",
264 ptable->parts[i].name);
265 dt_ret = DT_OP_FAILURE;
266 goto dev_tree_add_ptable_nodes_err;
267 }
268
269 }
270
271dev_tree_add_ptable_nodes_err:
Deepa Dinamani52aca8d2013-02-05 11:41:41 -0800272 dprintf(INFO, "Add %d flash partitions to dt: done\n", ptable->count);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700273 free(ptn_name_array);
274 return dt_ret;
275}
276
277/* Top level function to add flash ptable info to the device tree. */
278static int dev_tree_add_flash_ptable(void *fdt)
279{
280 uint32_t offset;
281 int ret;
282 int dt_ret = DT_OP_SUCCESS;
283
284 dt_ret = dev_tree_get_flash_node_offset(fdt);
285
286 if (dt_ret < 0)
287 {
288 dt_ret = DT_OP_FAILURE;
289 goto dev_tree_add_flash_ptable_err;
290 }
291
292 offset = dt_ret;
293
294 /* Add address and size cell properties. */
295 ret = fdt_setprop_u32(fdt, offset, (const char*)"#address-cells", 1);
296
297 if (ret != 0)
298 {
299 dprintf(CRITICAL, "Unable to add #address-cells property. \n");
300 dt_ret = DT_OP_FAILURE;
301 goto dev_tree_add_flash_ptable_err;
302 }
303
304 ret = fdt_setprop_u32(fdt, offset, (const char*)"#size-cells", 1);
305
306 if (ret != 0)
307 {
308 dprintf(CRITICAL, "Unable to add #size-cells property. \n");
309 dt_ret = DT_OP_FAILURE;
310 goto dev_tree_add_flash_ptable_err;
311 }
312
313 ret = dev_tree_add_ptable_nodes(fdt, offset);
314
315 if (ret < 0)
316 {
317 dprintf(CRITICAL, "Unable to add #size-cells property. \n");
318 dt_ret = DT_OP_FAILURE;
319 goto dev_tree_add_flash_ptable_err;
320 }
321
322dev_tree_add_flash_ptable_err:
323 return dt_ret;
324}
325
326/* Function to add the first RAM partition info to the device tree.
327 * Note: The function replaces the reg property in the "/memory" node
328 * with the addr and size provided.
329 */
330int dev_tree_add_first_mem_info(uint32_t *fdt, uint32_t offset, uint32_t addr, uint32_t size)
331{
332 int ret;
333
334 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
335
336 if (ret)
337 {
338 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
339 ret);
340 }
341
342
343 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
344
345 if (ret)
346 {
347 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
348 ret);
349 }
350
351 return ret;
352}
353
354/* Function to add the subsequent RAM partition info to the device tree. */
355int dev_tree_add_mem_info(void *fdt, uint32_t offset, uint32_t addr, uint32_t size)
356{
357 static int mem_info_cnt = 0;
358 int ret;
359
360 if (!mem_info_cnt)
361 {
362 /* Replace any other reg prop in the memory node. */
363 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
364 mem_info_cnt = 1;
365 }
366 else
367 {
368 /* Append the mem info to the reg prop for subsequent nodes. */
369 ret = fdt_appendprop_u32(fdt, offset, "reg", addr);
370 }
371
372 if (ret)
373 {
374 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
375 ret);
376 }
377
378
379 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
380
381 if (ret)
382 {
383 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
384 ret);
385 }
386
387 return ret;
388}
389
390/* Top level function that updates the device tree. */
391int update_device_tree(void *fdt, const char *cmdline,
392 void *ramdisk, uint32_t ramdisk_size)
393{
394 int ret = 0;
395 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700396
397 /* Check the device tree header */
398 ret = fdt_check_header(fdt);
399 if (ret)
400 {
401 dprintf(CRITICAL, "Invalid device tree header \n");
402 return ret;
403 }
404
405 /* Get offset of the memory node */
406 ret = fdt_path_offset(fdt, "/memory");
407 if (ret < 0)
408 {
409 dprintf(CRITICAL, "Could not find memory node.\n");
410 return ret;
411 }
412
413 offset = ret;
414
415 ret = target_dev_tree_mem(fdt, offset);
416 if(ret)
417 {
418 dprintf(CRITICAL, "ERROR: Cannot update memory node\n");
419 return ret;
420 }
421
422 /* Skip NAND partition nodes for eMMC boot */
423 if (!target_is_emmc_boot()){
424 dev_tree_add_flash_ptable(fdt);
425 }
426
427 /* Get offset of the chosen node */
428 ret = fdt_path_offset(fdt, "/chosen");
429 if (ret < 0)
430 {
431 dprintf(CRITICAL, "Could not find chosen node.\n");
432 return ret;
433 }
434
435 offset = ret;
436 /* Adding the cmdline to the chosen node */
Amol Jadi10c7d1c2013-01-25 13:24:29 -0800437 ret = fdt_setprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700438 if (ret)
439 {
440 dprintf(CRITICAL, "ERROR: Cannot update chosen node [bootargs]\n");
441 return ret;
442 }
443
444 /* Adding the initrd-start to the chosen node */
445 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-start", (uint32_t)ramdisk);
446 if (ret)
447 {
448 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-start]\n");
449 return ret;
450 }
451
452 /* Adding the initrd-end to the chosen node */
453 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-end", ((uint32_t)ramdisk + ramdisk_size));
454 if (ret)
455 {
456 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-end]\n");
457 return ret;
458 }
459
460 fdt_pack(fdt);
461
462 return ret;
463}