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