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