blob: 40f1f30315ea3e97b3c56560c5fd67651fdba65a [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
174 /* Need to add partitions in reverse order since libfdt adds
175 * new nodes on the top.
176 * Kernel looks to mount the partitions in the order specified in
177 * the partition.xml in the meta build.
178 */
179 for (i = (ptable->count - 1); i >= 0; i--)
180 {
181 /* Add the partition node. */
182 if (itoa(ptable->parts[i].start * blk_size, ptn_name_array + n - 1, sizeof(uint32_t) * 2 + 1, 16) < 0)
183 {
184 dprintf(CRITICAL, "String len exceeded for itoa\n");
185 return -1;
186 }
187
188 strcpy((char *)array, (const char*)"/qcom,mtd-partitions/");
189 strcat((char*)array, (const char*)ptn_name_array);
190
191 ret = fdt_add_subnode(fdt, parent_offset, (const char*)ptn_name_array);
192
193 if (ret < 0)
194 {
195 dprintf(CRITICAL, "Unable to add partition node: %s.\n",
196 ptn_name_array);
197 dt_ret = DT_OP_FAILURE;
198 goto dev_tree_add_ptable_nodes_err;
199 }
200
201 dt_ret = fdt_path_offset(fdt, (const char*)array);
202
203 if (dt_ret < 0)
204 {
205 dprintf(CRITICAL, "Unable to calculate parition node offset: %s\n",
206 ptn_name_array);
207 dt_ret = DT_OP_FAILURE;
208 goto dev_tree_add_ptable_nodes_err;
209 }
210
211 node_offset = dt_ret;
212
213 /* Add the partition name as label. */
214 ret = fdt_setprop_string(fdt, node_offset, (const char*)"label", (const char*)ptable->parts[i].name);
215
216 if (ret != 0)
217 {
218 dprintf(CRITICAL, "Unable to add label property: %s.\n",
219 ptable->parts[i].name);
220 dt_ret = DT_OP_FAILURE;
221 goto dev_tree_add_ptable_nodes_err;
222 }
223
224 /* Add the reg values. */
225 ret = fdt_setprop_u32(fdt, node_offset, (const char*)"reg", ptable->parts[i].start * blk_size);
226
227 if (ret != 0)
228 {
229 dprintf(CRITICAL, "Unable to add reg property. %s\n",
230 ptable->parts[i].name);
231 dt_ret = DT_OP_FAILURE;
232 goto dev_tree_add_ptable_nodes_err;
233 }
234
235 ret = fdt_appendprop_u32(fdt, node_offset, (const char*)"reg", ptable->parts[i].length * blk_size);
236
237 if (ret != 0)
238 {
239 dprintf(CRITICAL, "Unable to add reg property. %s\n",
240 ptable->parts[i].name);
241 dt_ret = DT_OP_FAILURE;
242 goto dev_tree_add_ptable_nodes_err;
243 }
244
245 }
246
247dev_tree_add_ptable_nodes_err:
248 free(ptn_name_array);
249 return dt_ret;
250}
251
252/* Top level function to add flash ptable info to the device tree. */
253static int dev_tree_add_flash_ptable(void *fdt)
254{
255 uint32_t offset;
256 int ret;
257 int dt_ret = DT_OP_SUCCESS;
258
259 dt_ret = dev_tree_get_flash_node_offset(fdt);
260
261 if (dt_ret < 0)
262 {
263 dt_ret = DT_OP_FAILURE;
264 goto dev_tree_add_flash_ptable_err;
265 }
266
267 offset = dt_ret;
268
269 /* Add address and size cell properties. */
270 ret = fdt_setprop_u32(fdt, offset, (const char*)"#address-cells", 1);
271
272 if (ret != 0)
273 {
274 dprintf(CRITICAL, "Unable to add #address-cells property. \n");
275 dt_ret = DT_OP_FAILURE;
276 goto dev_tree_add_flash_ptable_err;
277 }
278
279 ret = fdt_setprop_u32(fdt, offset, (const char*)"#size-cells", 1);
280
281 if (ret != 0)
282 {
283 dprintf(CRITICAL, "Unable to add #size-cells property. \n");
284 dt_ret = DT_OP_FAILURE;
285 goto dev_tree_add_flash_ptable_err;
286 }
287
288 ret = dev_tree_add_ptable_nodes(fdt, offset);
289
290 if (ret < 0)
291 {
292 dprintf(CRITICAL, "Unable to add #size-cells property. \n");
293 dt_ret = DT_OP_FAILURE;
294 goto dev_tree_add_flash_ptable_err;
295 }
296
297dev_tree_add_flash_ptable_err:
298 return dt_ret;
299}
300
301/* Function to add the first RAM partition info to the device tree.
302 * Note: The function replaces the reg property in the "/memory" node
303 * with the addr and size provided.
304 */
305int dev_tree_add_first_mem_info(uint32_t *fdt, uint32_t offset, uint32_t addr, uint32_t size)
306{
307 int ret;
308
309 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
310
311 if (ret)
312 {
313 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
314 ret);
315 }
316
317
318 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
319
320 if (ret)
321 {
322 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
323 ret);
324 }
325
326 return ret;
327}
328
329/* Function to add the subsequent RAM partition info to the device tree. */
330int dev_tree_add_mem_info(void *fdt, uint32_t offset, uint32_t addr, uint32_t size)
331{
332 static int mem_info_cnt = 0;
333 int ret;
334
335 if (!mem_info_cnt)
336 {
337 /* Replace any other reg prop in the memory node. */
338 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
339 mem_info_cnt = 1;
340 }
341 else
342 {
343 /* Append the mem info to the reg prop for subsequent nodes. */
344 ret = fdt_appendprop_u32(fdt, offset, "reg", addr);
345 }
346
347 if (ret)
348 {
349 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
350 ret);
351 }
352
353
354 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
355
356 if (ret)
357 {
358 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
359 ret);
360 }
361
362 return ret;
363}
364
365/* Top level function that updates the device tree. */
366int update_device_tree(void *fdt, const char *cmdline,
367 void *ramdisk, uint32_t ramdisk_size)
368{
369 int ret = 0;
370 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700371
372 /* Check the device tree header */
373 ret = fdt_check_header(fdt);
374 if (ret)
375 {
376 dprintf(CRITICAL, "Invalid device tree header \n");
377 return ret;
378 }
379
380 /* Get offset of the memory node */
381 ret = fdt_path_offset(fdt, "/memory");
382 if (ret < 0)
383 {
384 dprintf(CRITICAL, "Could not find memory node.\n");
385 return ret;
386 }
387
388 offset = ret;
389
390 ret = target_dev_tree_mem(fdt, offset);
391 if(ret)
392 {
393 dprintf(CRITICAL, "ERROR: Cannot update memory node\n");
394 return ret;
395 }
396
397 /* Skip NAND partition nodes for eMMC boot */
398 if (!target_is_emmc_boot()){
399 dev_tree_add_flash_ptable(fdt);
400 }
401
402 /* Get offset of the chosen node */
403 ret = fdt_path_offset(fdt, "/chosen");
404 if (ret < 0)
405 {
406 dprintf(CRITICAL, "Could not find chosen node.\n");
407 return ret;
408 }
409
410 offset = ret;
411 /* Adding the cmdline to the chosen node */
Amol Jadi10c7d1c2013-01-25 13:24:29 -0800412 ret = fdt_setprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700413 if (ret)
414 {
415 dprintf(CRITICAL, "ERROR: Cannot update chosen node [bootargs]\n");
416 return ret;
417 }
418
419 /* Adding the initrd-start to the chosen node */
420 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-start", (uint32_t)ramdisk);
421 if (ret)
422 {
423 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-start]\n");
424 return ret;
425 }
426
427 /* Adding the initrd-end to the chosen node */
428 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-end", ((uint32_t)ramdisk + ramdisk_size));
429 if (ret)
430 {
431 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-end]\n");
432 return ret;
433 }
434
435 fdt_pack(fdt);
436
437 return ret;
438}