blob: e040fa6fe286a4a3682dd49ae63a2098e564e69f [file] [log] [blame]
/*
* Copyright (c) 2015-2017, 2020 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <common.h>
#include <asm/arch-qca-common/smem.h>
#include <asm/arch-qca-common/scm.h>
#include <jffs2/load_kernel.h>
#include <fdtdec.h>
#include <stdlib.h>
#include "fdt_info.h"
DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_IPQ_FDT_FIXUP
#define FDT_EDIT "fdtedit"
/* Buffer size to hold numbers from 0-99 + 1 NULL character */
#define NUM_BUF_SIZE 3
#endif
/*
* Don't have this as a '.bss' variable. The '.bss' and '.rel.dyn'
* sections seem to overlap.
*
* $ arm-none-linux-gnueabi-objdump -h u-boot
* . . .
* 8 .rel.dyn 00004ba8 40630b0c 40630b0c 00038b0c 2**2
* CONTENTS, ALLOC, LOAD, READONLY, DATA
* 9 .bss 0000559c 40630b0c 40630b0c 00000000 2**3
* ALLOC
* . . .
*
* board_early_init_f() initializes this variable, resulting in one
* of the relocation entries present in '.rel.dyn' section getting
* corrupted. Hence, when relocate_code()'s 'fixrel' executes, it
* patches a wrong address, which incorrectly modifies some global
* variable resulting in a crash.
*
* Moral of the story: Global variables that are written before
* relocate_code() gets executed cannot be in '.bss'
*/
#ifdef CONFIG_OF_BOARD_SETUP
extern int fdt_node_set_part_info(void *blob, int parent_offset,
struct mtd_device *dev);
struct flash_node_info {
const char *compat; /* compatible string */
int type; /* mtd flash type */
int idx; /* flash index */
};
int ipq_fdt_fixup_spi_nor_params(void *blob)
{
int nodeoff, ret;
qca_smem_flash_info_t sfi;
uint32_t val;
/* Get flash parameters from smem */
smem_get_boot_flash(&sfi.flash_type,
&sfi.flash_index,
&sfi.flash_chip_select,
&sfi.flash_block_size,
&sfi.flash_density);
nodeoff = fdt_node_offset_by_compatible(blob, -1, "n25q128a11");
if (nodeoff < 0) {
printf("fdt-fixup: unable to find compatible node\n");
return nodeoff;
}
val = cpu_to_fdt32(sfi.flash_block_size);
ret = fdt_setprop(blob, nodeoff, "sector-size",
&val, sizeof(uint32_t));
if (ret) {
printf("fdt-fixup: unable to set sector size(%d)\n", ret);
return -1;
}
if (sfi.flash_density != 0) {
val = cpu_to_fdt32(sfi.flash_density);
ret = fdt_setprop(blob, nodeoff, "density",
&val, sizeof(uint32_t));
if (ret) {
printf("fdt-fixup: unable to set density(%d)\n", ret);
return -1;
}
}
return 0;
}
void ipq_fdt_fixup_version(void *blob)
{
int nodeoff, ret;
char ver[OEM_VERSION_STRING_LENGTH + VERSION_STRING_LENGTH + 1];
uint32_t machid;
nodeoff = fdt_path_offset(blob, "/");
if (nodeoff < 0) {
debug("fdt-fixup: fdt fixup unable to find root node\n");
return;
}
machid = smem_get_board_platform_type();
if (machid) {
ret = fdt_setprop(blob, nodeoff, "machid",
&machid, sizeof(uint32_t));
if (ret)
debug("fdt-fixup: unable to set machid(%d)\n", ret);
}
if (!smem_get_build_version(ver, sizeof(ver), BOOT_VERSION)) {
debug("BOOT Build Version: %s\n", ver);
ret = fdt_setprop(blob, nodeoff, "boot_version",
ver, strlen(ver));
if (ret)
debug("fdt-fixup: unable to set Boot version(%d)\n", ret);
}
/* ipq806x doesn't have image version table in SMEM */
else if (!ipq_smem_get_boot_version(ver, sizeof(ver))) {
debug("BOOT Build Version: %s\n", ver);
ret = fdt_setprop(blob, nodeoff, "boot_version",
ver, strlen(ver));
if (ret)
debug("fdt-fixup: unable to set Boot version(%d)\n", ret);
}
if (!smem_get_build_version(ver, sizeof(ver), TZ_VERSION)) {
debug("TZ Build Version: %s\n", ver);
ret = fdt_setprop(blob, nodeoff, "tz_version",
ver, strlen(ver));
if (ret)
debug("fdt-fixup: unable to set TZ version(%d)\n", ret);
}
/* for ipq806x, get the tz_version through scm_call */
else if (!ipq_get_tz_version(ver, sizeof(ver))) {
debug("TZ Build Version: %s\n", ver);
ret = fdt_setprop(blob, nodeoff, "tz_version",
ver, strlen(ver));
if (ret)
debug("fdt-fixup: unable to set TZ version(%d)\n", ret);
}
#ifdef RPM_VERSION
if (!smem_get_build_version(ver, sizeof(ver), RPM_VERSION)) {
debug("RPM Build Version: %s\n", ver);
ret = fdt_setprop(blob, nodeoff, "rpm_version",
ver, strlen(ver));
if (ret)
debug("fdt-fixup: unable to set RPM version(%d)\n", ret);
}
#endif /* RPM_VERSION */
}
#ifdef CONFIG_IPQ_TINY
#define OFFSET_NOT_SPECIFIED (~0llu)
struct reg_cell {
unsigned int r0;
unsigned int r1;
};
extern int fdt_del_partitions(void *blob, int parent_offset);
int parse_nor_partition(const char *const partdef,
const char **ret,
struct part_info **retpart);
void free_parse(struct list_head *head);
static void ipq_nor_fdt_fixup(void *blob, struct flash_node_info *ni)
{
int parent_offset;
struct part_info *part, *pPartInfo;
struct reg_cell cell;
int off, ndepth = 0;
int part_num, ret, newoff;
char buf[64];
int offset = 0;
struct list_head *pentry;
LIST_HEAD(tmp_list);
const char *pMtdparts = getenv("mtdparts");
if (!pMtdparts)
return;
for (; ni->compat; ni++) {
parent_offset = fdt_node_offset_by_compatible(
blob, -1, ni->compat);
if (parent_offset != -FDT_ERR_NOTFOUND)
break;
}
if (parent_offset == -FDT_ERR_NOTFOUND){
printf(" compatible not found \n");
return;
}
ret = fdt_del_partitions(blob, parent_offset);
if (ret < 0)
return;
/*
* Check if it is nand {}; subnode, adjust
* the offset in this case
*/
off = fdt_next_node(blob, parent_offset, &ndepth);
if (off > 0 && ndepth == 1)
parent_offset = off;
if (strncmp(pMtdparts, "mtdparts=", 9) == 0) {
pMtdparts += 9;
pMtdparts = strchr(pMtdparts, ':');
pMtdparts++;
} else {
printf("mtdparts variable doesn't start with 'mtdparts='\n");
return;
}
part_num = 0;
while (pMtdparts && (*pMtdparts != '\0') && (*pMtdparts != ';')) {
if ((parse_nor_partition(pMtdparts, &pMtdparts, &pPartInfo) != 0))
break;
/* calculate offset when not specified */
if (pPartInfo->offset == OFFSET_NOT_SPECIFIED)
pPartInfo->offset = offset;
else
offset = pPartInfo->offset;
offset += pPartInfo->size;
/* partition is ok, add it to the list */
list_add_tail(&pPartInfo->link, &tmp_list);
}
list_for_each_prev(pentry, &tmp_list) {
part = list_entry(pentry, struct part_info, link);
debug("%2d: %-20s0x%08llx\t0x%08llx\t%d\n",
part_num, part->name, part->size,
part->offset, part->mask_flags);
snprintf(buf, sizeof(buf), "partition@%llx", part->offset);
add_sub:
ret = fdt_add_subnode(blob, parent_offset, buf);
if (ret == -FDT_ERR_NOSPACE) {
ret = fdt_increase_size(blob, 512);
if (!ret)
goto add_sub;
else
goto err_size;
} else if (ret < 0) {
printf("Can't add partition node: %s\n",
fdt_strerror(ret));
return;
}
newoff = ret;
/* Check MTD_WRITEABLE_CMD flag */
if (pPartInfo->mask_flags & 1) {
add_ro:
ret = fdt_setprop(blob, newoff, "read_only", NULL, 0);
if (ret == -FDT_ERR_NOSPACE) {
ret = fdt_increase_size(blob, 512);
if (!ret)
goto add_ro;
else
goto err_size;
} else if (ret < 0)
goto err_prop;
}
cell.r0 = cpu_to_fdt32(part->offset);
cell.r1 = cpu_to_fdt32(part->size);
add_reg:
ret = fdt_setprop(blob, newoff, "reg", &cell, sizeof(cell));
if (ret == -FDT_ERR_NOSPACE) {
ret = fdt_increase_size(blob, 512);
if (!ret)
goto add_reg;
else
goto err_size;
} else if (ret < 0)
goto err_prop;
add_label:
ret = fdt_setprop_string(blob, newoff, "label", part->name);
if (ret == -FDT_ERR_NOSPACE) {
ret = fdt_increase_size(blob, 512);
if (!ret)
goto add_label;
else
goto err_size;
} else if (ret < 0)
goto err_prop;
part_num++;
}
goto remove_list;
err_size:
printf("Can't increase blob size: %s\n", fdt_strerror(ret));
goto remove_list;
err_prop:
printf("Can't add property: %s\n", fdt_strerror(ret));
goto remove_list;
remove_list:
free_parse(&tmp_list);
return;
}
#else
void ipq_fdt_fixup_mtdparts(void *blob, struct flash_node_info *ni)
{
struct mtd_device *dev;
char *parts;
int noff;
parts = getenv("mtdparts");
if (!parts)
return;
if (mtdparts_init() != 0)
return;
for (; ni->compat; ni++) {
noff = fdt_node_offset_by_compatible(blob, -1, ni->compat);
while (noff != -FDT_ERR_NOTFOUND) {
dev = device_find(ni->type, ni->idx);
if (dev) {
if (fdt_node_set_part_info(blob, noff, dev))
return; /* return on error */
}
/* Jump to next flash node */
noff = fdt_node_offset_by_compatible(blob, noff,
ni->compat);
}
}
}
#endif
void ipq_fdt_mem_rsvd_fixup(void *blob)
{
u32 dload;
int parentoff, nodeoff, ret, i;
dload = htonl(DLOAD_DISABLE);
/* Reserve only the TZ and SMEM memory region and free the rest */
parentoff = fdt_path_offset(blob, rsvd_node);
if (parentoff >= 0) {
for (i = 0; del_node[i]; i++) {
nodeoff = fdt_subnode_offset(blob, parentoff,
del_node[i]);
if (nodeoff < 0) {
debug("fdt-fixup: unable to findnode (%s)\n",
del_node[i]);
continue;
}
ret = fdt_del_node(blob, nodeoff);
if (ret != 0)
debug("fdt-fixup: unable to delete node (%s)\n",
del_node[i]);
}
for (i = 0; add_fdt_node[i].nodename; i++) {
nodeoff = fdt_add_subnode(blob, parentoff,
add_fdt_node[i].nodename);
if (nodeoff < 0) {
debug("fdt-fixup: unable to add subnode (%s)\n",
add_fdt_node[i].nodename);
continue;
}
ret = fdt_setprop(blob, nodeoff, "no-map", NULL, 0);
if (ret != 0)
debug("fdt-fixup: unable to set property\n");
ret = fdt_setprop(blob, nodeoff, "reg",
add_fdt_node[i].val,
sizeof(add_fdt_node[i].val));
if (ret != 0)
debug("fdt-fixup: unable to set property\n");
}
} else {
debug("fdt-fixup: unable to find node \n");
}
/* Set the dload_status to DLOAD_DISABLE */
nodeoff = fdt_path_offset(blob, "/soc/qca,scm_restart_reason");
if (nodeoff < 0) {
nodeoff = fdt_path_offset(blob, "/qti,scm_restart_reason");
if (nodeoff < 0) {
debug("fdt-fixup: unable to find compatible node\n");
return;
}
}
ret = fdt_setprop(blob, nodeoff, "dload_status", &dload, sizeof(dload));
if (ret != 0) {
debug("fdt-fixup: unable to find compatible node\n");
return;
}
}
struct vlan_tag {
unsigned int r0;
unsigned int r1;
};
struct eth_param{
int nodeoff;
int mdio_addr;
int poll;
int speed;
int duplex;
unsigned long gmac_no;
};
static void ipq40xx_set_setprop(void *blob, int nodeoff, unsigned long gmac_no,
char *str, int val)
{
int ret;
ret = fdt_setprop(blob, nodeoff, str, &val, sizeof(val));
if (ret)
debug("unable to set property %s for %lu with error %d\n",
str, gmac_no, ret);
}
static void ipq40xx_populate_eth_params(void *blob, struct eth_param *port)
{
ipq40xx_set_setprop(blob, port->nodeoff, port->gmac_no,
"qcom,phy_mdio_addr", htonl(port->mdio_addr));
ipq40xx_set_setprop(blob, port->nodeoff, port->gmac_no,
"qcom,poll_required", htonl(port->poll));
ipq40xx_set_setprop(blob, port->nodeoff, port->gmac_no,
"qcom,forced_speed", htonl(port->speed));
ipq40xx_set_setprop(blob, port->nodeoff, port->gmac_no,
"qcom,forced_duplex", htonl(port->duplex));
}
/*
* Logic to patch Ethernet params.
*/
static int ipq40xx_patch_eth_params(void *blob, unsigned long gmac_no)
{
int nodeoff, nodeoff_c;
int ret, i;
struct vlan_tag vlan;
struct eth_param port_config;
const char *eth2_prop[] = {"/soc/edma/gmac2", "/soc/edma/gmac3",
"/soc/edma/gmac4"};
const char *alias_prop[] = {"ethernet2", "ethernet3", "ethernet4"};
const char *gmac_node[] = {"gmac2", "gmac3", "gmac4"};
nodeoff = fdt_path_offset(blob, "/aliases");
if (nodeoff < 0) {
printf("ipq: fdt fixup unable to find compatible node\n");
return -1;
} else {
debug("Node Found\n");
}
for (i = 0; i < (gmac_no - 2); i++) {
ret = fdt_setprop(blob, nodeoff, alias_prop[i],
eth2_prop[i], (strlen(eth2_prop[i]) + 1));
if (ret)
debug("%d: unable to patch alias\n", ret);
nodeoff_c = fdt_path_offset(blob, "/soc/edma");
if (nodeoff_c < 0) {
printf("ipq: unable to find compatiable edma node\n");
return -1;
}
ret = fdt_add_subnode(blob, nodeoff_c, gmac_node[i]);
if (ret < 0)
debug("%d: unable to add node\n", ret);
}
switch (gmac_no) {
case 3:
nodeoff_c = fdt_path_offset(blob, "/soc/edma/gmac1");
if (nodeoff_c < 0) {
printf("ipq: unable to find compatiable edma node\n");
return -1;
}
vlan.r0 = htonl(0x1);
vlan.r1 = htonl(0x10);
ret = fdt_setprop(blob, nodeoff_c, "vlan_tag",
&vlan, sizeof(vlan));
if (ret)
debug("%d: unable to set property\n", ret);
nodeoff_c = fdt_path_offset(blob, "/soc/edma/gmac2");
if (nodeoff_c < 0) {
printf("ipq: unable to find compatiable edma node\n");
return -1;
}
vlan.r0 = htonl(0x3);
vlan.r1 = htonl(0xE);
ret = fdt_setprop(blob, nodeoff_c, "vlan_tag",
&vlan, sizeof(vlan));
if (ret)
debug("%d: unable to set property\n", ret);
break;
case 4:
nodeoff_c = fdt_path_offset(blob, "/soc/edma/gmac1");
if (nodeoff_c < 0) {
printf("ipq: unable to find compatiable edma node\n");
return -1;
}
vlan.r0 = htonl(0x1);
vlan.r1 = htonl(0x10);
ret = fdt_setprop(blob, nodeoff_c, "vlan_tag",
&vlan, sizeof(vlan));
if (ret)
debug("%d: unable to set property\n", ret);
nodeoff_c = fdt_path_offset(blob, "/soc/edma/gmac2");
if (nodeoff_c < 0) {
printf("ipq: unable to find compatiable edma node\n");
return -1;
}
vlan.r0 = htonl(0x3);
vlan.r1 = htonl(0x8);
ret = fdt_setprop(blob, nodeoff_c, "vlan_tag",
&vlan, sizeof(vlan));
if (ret)
debug("%d: unable to set property\n", ret);
nodeoff_c = fdt_path_offset(blob, "/soc/edma/gmac3");
if (nodeoff_c < 0) {
printf("ipq: unable to find compatiable edma node\n");
return -1;
}
vlan.r0 = htonl(0x4);
vlan.r1 = htonl(0x6);
ret = fdt_setprop(blob, nodeoff_c, "vlan_tag",
&vlan, sizeof(vlan));
if (ret)
debug("%d: unable to set property\n", ret);
break;
case 5:
nodeoff_c = fdt_path_offset(blob, "/soc/edma/gmac1");
if (nodeoff_c < 0) {
printf("ipq: unable to find compatiable edma node\n");
return -1;
}
vlan.r0 = htonl(0x1);
vlan.r1 = htonl(0x10);
ret = fdt_setprop(blob, nodeoff_c, "vlan_tag",
&vlan, sizeof(vlan));
if (ret)
debug("%d: unable to set property\n", ret);
port_config.nodeoff = nodeoff_c;
port_config.mdio_addr = 3;
port_config.poll = 1;
port_config.speed = 1000;
port_config.duplex = 1;
port_config.gmac_no = gmac_no;
ipq40xx_populate_eth_params(blob, &port_config);
nodeoff_c = fdt_path_offset(blob, "/soc/edma/gmac2");
if (nodeoff_c < 0) {
printf("ipq: unable to find compatiable edma node\n");
return -1;
}
vlan.r0 = htonl(0x3);
vlan.r1 = htonl(0x8);
ret = fdt_setprop(blob, nodeoff_c, "vlan_tag",
&vlan, sizeof(vlan));
if (ret)
debug("%d: unable to set property\n", ret);
port_config.nodeoff = nodeoff_c;
port_config.mdio_addr = 2;
port_config.poll = 1;
port_config.speed = 1000;
port_config.duplex = 1;
port_config.gmac_no = gmac_no;
ipq40xx_populate_eth_params(blob, &port_config);
nodeoff_c = fdt_path_offset(blob, "/soc/edma/gmac3");
if (nodeoff_c < 0) {
printf("ipq: unable to find compatiable edma node\n");
return -1;
}
vlan.r0 = htonl(0x4);
vlan.r1 = htonl(0x4);
ret = fdt_setprop(blob, nodeoff_c, "vlan_tag",
&vlan, sizeof(vlan));
if (ret)
debug("%d: unable to set property\n", ret);
port_config.nodeoff = nodeoff_c;
port_config.mdio_addr = 1;
port_config.poll = 1;
port_config.speed = 1000;
port_config.duplex = 1;
port_config.gmac_no = gmac_no;
ipq40xx_populate_eth_params(blob, &port_config);
nodeoff_c = fdt_path_offset(blob, "/soc/edma/gmac4");
if (nodeoff_c < 0) {
printf("ipq: unable to find compatiable edma node\n");
return -1;
}
vlan.r0 = htonl(0x5);
vlan.r1 = htonl(0x2);
ret = fdt_setprop(blob, nodeoff_c, "vlan_tag",
&vlan, sizeof(vlan));
if (ret)
debug("%d: unable to set property\n", ret);
port_config.nodeoff = nodeoff_c;
port_config.mdio_addr = 0;
port_config.poll = 1;
port_config.speed = 1000;
port_config.duplex = 1;
port_config.gmac_no = gmac_no;
ipq40xx_populate_eth_params(blob, &port_config);
break;
}
nodeoff = fdt_node_offset_by_compatible(blob,
-1, "qcom,ess-edma");
if (nodeoff < 0) {
printf("ipq: unable to find compatible edma node\n");
return -1;
}
gmac_no = htonl(gmac_no);
ret = fdt_setprop(blob, nodeoff, "qcom,num_gmac",
&gmac_no, sizeof(gmac_no));
if (ret)
debug("%d: unable to set property\n", ret);
return 0;
}
#ifdef CONFIG_IPQ_FDT_FIXUP
/* setenv fdteditnum <num> - here <num> represents number of envs to parse
* Note: without setting 'fdteditnum' fdtedit envs will not parsed
*
* fdtedit<num> <node>%<property>%<node_value> - dts patching env format
* here '%' is separator; <num> can be between 1 to 99;
*
* 1. To change add/change a particular property of a node:
* setenv fdtedit0 <node_path>%<property>%<value>
*
* This can be used to add properties which doesn't have any value associated
* eg: qca,secure; property of q6v5_wcss@CD00000 node can be added as:
* setenv fdtedit0 /soc/q6v5_wcss@CD00000/%qca,secure%1
* other eg:
* fdtedit0=/soc/q6v5_wcss@CD00000%qca,sec-reset-cmd%0x19
* fdtedit1=/soc/usb3@8A00000/dwc3@8A00000%dr_mode%?peripheral
* fdtedit2=/soc/qcom,gadget_diag@0/%status%?ok
*
* 2. To delete a property of a node:
* setenv fdtedit0 <node_path>%delete%<property>
* example:
* fdtedit0=/soc/q6v5_wcss@CD00000%delete%?qca,secure
*
* The last param in both add or delete case, if it is a string, it should
* start with '?' else if it is a number, it can be put directly.
* check above examples for reference.
*
* 3. To add 32bit or 64bit array values:
* setenv fdtedit0 <node_path>%<bit_value>?<num_values>?<property_name>%<value1>?<value2>?<value3>?..
* <bit_value> can be 32 / 64; <num_values> is number of array elements
* to be patched; <property_name> is the actual name of the property to
* be patched; each array value has to be separated by '?'
* for reg = <addr> <size>; <num_values> is 2 in this case
* example:
* setenv fdtedit0 /soc/dbm@0x8AF8000/%32?2?reg%0x8AF8000?0x500
* setenv fdtedit1 /soc/pci@20000000/%32?2?bus-range%0xee?0xee
* setenv fdtedit2 /soc/usb3@8A00000/%32?4?reg%0x8AF8600?0x200?0x8A00000?0xcb00
* setenv fdtedit3 /reserved-memory/tzapp@49B00000/%64?2?reg%0x49A00000?0x500000
*/
void parse_fdt_fixup(char* buf, void *blob)
{
int nodeoff, value, ret, num_values, i;
char *node, *property, *node_value, *sliced_string;
bool if_string = true, bit32 = true;
u32 *values32;
u64 *values64;
/* env is split into <node>%<property>%<node_value>. '%' is separator */
node = strsep(&buf, "%");
property = strsep(&buf, "%");
node_value = strsep(&buf, "%");
debug("node: %s property: %s node_value: %s\n", node, property, node_value);
/* if '?' is present then node_value is string;
* else, node_value is 32bit value
*/
if (node_value && node_value[0] != '?') {
if_string = false;
value = simple_strtoul(node_value, NULL, 10);
} else {
/* skip '?' */
node_value++;
}
nodeoff = fdt_path_offset(blob, node);
if (nodeoff < 0) {
printf("%s: unable to find node '%s'\n", __func__, node);
return;
}
if (!strncmp(property, "delete", strlen("delete"))) {
/* handle property deletes */
ret = fdt_delprop(blob, nodeoff, node_value);
if (ret) {
printf("%s: unable to delete %s\n", __func__, node_value);
return;
}
} else if (!strncmp(property, "32", strlen("32")) || !strncmp(property, "64", strlen("64"))) {
/* if property name starts with '32' or '64', then it is used
* for patching array of 32bit / 64bit values correspondingly.
* 32bit patching is usually used to patch reg = <addr> <size>;
* but could also be used to patch multiple addresses & sizes
* <property> = <addr1> <size1> <addr2> <size2> ..
* 64bit patching is usually used to patch reserved memory nodes
*/
sliced_string = strsep(&property, "?");
if (simple_strtoul(sliced_string, NULL, 10) == 64)
bit32 = false;
/* get the number of array values */
sliced_string = strsep(&property, "?");
num_values = simple_strtoul(sliced_string, NULL, 10);
if (bit32 == true) {
values32 = malloc(num_values * sizeof(u32));
for (i = 0; i < num_values; i++) {
sliced_string = strsep(&node_value, "?");
values32[i] = cpu_to_fdt32(simple_strtoul(sliced_string, NULL, 10));
}
ret = fdt_setprop(blob, nodeoff, property, values32, num_values * sizeof(u32));
if (ret) {
printf("%s: failed to set prop %s\n", __func__, property);
return;
}
} else {
values64 = malloc(num_values * sizeof(u64));
for (i = 0; i < num_values; i++) {
sliced_string = strsep(&node_value, "?");
values64[i] = cpu_to_fdt64(simple_strtoul(sliced_string, NULL, 10));
}
ret = fdt_setprop(blob, nodeoff, property, values64, num_values * sizeof(u64));
if (ret) {
printf("%s: failed to set prop %s\n", __func__, property);
return;
}
}
} else if (!if_string) {
/* handle 32bit integer value patching */
ret = fdt_setprop_u32(blob, nodeoff, property, value);
if (ret) {
printf("%s: failed to set prop %s\n", __func__, property);
return;
}
} else {
/* handle string value patching
* usually used to patch status = "ok"; status = "disabled";
*/
ret = fdt_setprop(blob, nodeoff, property,
node_value,
(strlen(node_value) + 1));
if (ret) {
printf("%s: failed to set prop %s\n", __func__, property);
return;
}
}
}
/* check parse_fdt_fixup for detailed explanation */
void ipq_fdt_fixup(void *blob)
{
int i, fdteditnum;
char buf[sizeof(FDT_EDIT) + NUM_BUF_SIZE], num[NUM_BUF_SIZE];
char *s;
/* fdteditnum - defines the number of envs to parse
* starting from 0. eg: fdtedit0, fdtedit1, and so on.
*/
s = getenv("fdteditnum");
if (s)
fdteditnum = simple_strtoul(s, NULL, 10);
else
return;
printf("%s: fixup fdtedits\n", __func__);
for (i = 0; i <= fdteditnum; i++) {
/* Generate env names fdtedit0, fdtedit1,..fdteditn */
strlcpy(buf, FDT_EDIT, sizeof(buf));
snprintf(num, sizeof(num), "%d", i);
strlcat(buf, num, sizeof(buf));
s = getenv(buf);
if (s)
parse_fdt_fixup(s, blob);
}
}
#endif
__weak void fdt_fixup_sd_ldo_gpios_toggle(void *blob)
{
return;
}
__weak void fdt_low_memory_fixup(void *blob)
{
return;
}
__weak void fdt_fixup_cpr(void *blob)
{
return;
}
__weak void fdt_fixup_cpus_node(void * blob)
{
return;
}
__weak void fdt_fixup_set_dload_warm_reset(void *blob)
{
return;
}
__weak void fdt_fixup_set_qce_fixed_key(void *blob)
{
return;
}
__weak void fdt_fixup_set_qca_cold_reboot_enable(void *blob)
{
return;
}
__weak void fdt_fixup_wcss_rproc_for_atf(void *blob)
{
return;
}
__weak void fdt_fixup_art_format(void *blob)
{
return;
}
#ifdef CONFIG_IPQ_BT_SUPPORT
__weak void fdt_fixup_bt_running(void *blob)
{
return;
}
#endif
__weak void fdt_fixup_qpic(void *blob)
{
return;
}
void set_mtdids(void)
{
char mtdids[256];
if (getenv("mtdids") != NULL) {
/* mtdids env is already set, nothing to do */
return;
}
qca_smem_flash_info_t *sfi = &qca_smem_flash_info;
if (sfi->flash_type == SMEM_BOOT_SPI_FLASH) {
if (get_which_flash_param("rootfs") ||
((sfi->flash_secondary_type == SMEM_BOOT_NAND_FLASH) ||
(sfi->flash_secondary_type == SMEM_BOOT_QSPI_NAND_FLASH))) {
snprintf(mtdids, sizeof(mtdids),
"nand%d=nand%d,nand%d=" QCA_SPI_NOR_DEVICE,
is_spi_nand_available(),
is_spi_nand_available(),
CONFIG_SPI_FLASH_INFO_IDX);
} else {
snprintf(mtdids, sizeof(mtdids), "nand%d="
QCA_SPI_NOR_DEVICE, CONFIG_SPI_FLASH_INFO_IDX);
}
setenv("mtdids", mtdids);
} else if (((sfi->flash_type == SMEM_BOOT_NAND_FLASH) ||
(sfi->flash_type == SMEM_BOOT_QSPI_NAND_FLASH))) {
snprintf(mtdids, sizeof(mtdids), "nand0=nand0");
setenv("mtdids", mtdids);
}
}
/*
* For newer kernel that boot with device tree (3.14+), all of memory is
* described in the /memory node, including areas that the kernel should not be
* touching.
*
* By default, u-boot will walk the dram bank info and populate the /memory
* node; here, overwrite this behavior so we describe all of memory instead.
*/
int ft_board_setup(void *blob, bd_t *bd)
{
u64 memory_start = CONFIG_SYS_SDRAM_BASE;
u64 memory_size = gd->ram_size;
unsigned long gmac_no;
uint32_t flash_type;
char *s;
char *mtdparts = NULL;
char *addparts = NULL;
char parts_str[4096];
int len = sizeof(parts_str), ret;
qca_smem_flash_info_t *sfi = &qca_smem_flash_info;
int activepart = 0;
#ifdef CONFIG_IPQ_TINY
struct flash_node_info nodes[] = {
{ "n25q128a11", MTD_DEV_TYPE_NAND,
CONFIG_IPQ_SPI_NOR_INFO_IDX }
};
#else
struct flash_node_info nodes[] = {
{ "qcom,msm-nand", MTD_DEV_TYPE_NAND, 0 },
{ "qcom,qcom_nand", MTD_DEV_TYPE_NAND, 0 },
{ "qcom,ebi2-nandc-bam-v1.5.0", MTD_DEV_TYPE_NAND, 0 },
{ "qcom,ebi2-nandc-bam-v2.1.1", MTD_DEV_TYPE_NAND, 0 },
{ "qcom,ipq8074-nand", MTD_DEV_TYPE_NAND, 0 },
{ "spinand,mt29f", MTD_DEV_TYPE_NAND, 1 },
{ "n25q128a11", MTD_DEV_TYPE_NAND,
CONFIG_IPQ_SPI_NOR_INFO_IDX },
{ "s25fl256s1", MTD_DEV_TYPE_NAND, 1 },
{ NULL, 0, -1 }, /* Terminator */
};
#endif
fdt_fixup_memory_banks(blob, &memory_start, &memory_size, 1);
ipq_fdt_fixup_version(blob);
#ifndef CONFIG_QCA_APPSBL_DLOAD
ipq_fdt_mem_rsvd_fixup(blob);
#endif
if (((sfi->flash_type == SMEM_BOOT_NAND_FLASH) ||
(sfi->flash_type == SMEM_BOOT_QSPI_NAND_FLASH))) {
snprintf(parts_str, sizeof(parts_str), "mtdparts=nand0");
} else if (sfi->flash_type == SMEM_BOOT_SPI_FLASH) {
/* Patch NOR block size and density for
* generic probe case */
ipq_fdt_fixup_spi_nor_params(blob);
snprintf(parts_str,sizeof(parts_str), "mtdparts=" QCA_SPI_NOR_DEVICE);
if ((sfi->flash_secondary_type == SMEM_BOOT_NAND_FLASH) ||
(sfi->flash_secondary_type == SMEM_BOOT_QSPI_NAND_FLASH)) {
if(smem_bootconfig_info() == 0)
activepart = get_rootfs_active_partition();
if (!activepart) {
snprintf(parts_str, sizeof(parts_str),
"mtdparts=nand0:0x%x@0(rootfs),"
"0x%x@0x%x(rootfs_1);nand1",
IPQ_NAND_ROOTFS_SIZE,
IPQ_NAND_ROOTFS_SIZE, IPQ_NAND_ROOTFS_SIZE);
} else {
snprintf(parts_str, sizeof(parts_str),
"mtdparts=nand0:0x%x@0x%x(rootfs),"
"0x%x@0(rootfs_1);nand1",IPQ_NAND_ROOTFS_SIZE,
IPQ_NAND_ROOTFS_SIZE, IPQ_NAND_ROOTFS_SIZE);
}
}
}
mtdparts = parts_str;
if (mtdparts) {
qca_smem_part_to_mtdparts(mtdparts,len);
if (mtdparts[0] != '\0') {
addparts = getenv("addmtdparts");
if (addparts) {
debug("addmtdparts = %s\n", addparts);
strlcat(mtdparts, ",", sizeof(parts_str));
strlcat(mtdparts, addparts, sizeof(parts_str));
}
debug("mtdparts = %s\n", mtdparts);
setenv("mtdparts", mtdparts);
}
set_mtdids();
debug("MTDIDS: %s\n", getenv("mtdids"));
#ifdef CONFIG_IPQ_TINY
ipq_nor_fdt_fixup(blob, nodes);
#else
ipq_fdt_fixup_mtdparts(blob, nodes);
#endif
}
/* Add "flash_type" to root node of the devicetree*/
ret = get_current_flash_type(&flash_type);
if (!ret) {
ret = fdt_setprop(blob, 0, "flash_type", &flash_type,
sizeof(flash_type));
if (ret)
printf("%s: cannot set flash type %d\n", __func__, ret);
}
ipq_fdt_fixup_socinfo(blob);
s = (getenv("gmacnumber"));
if (s) {
strict_strtoul(s, 16, &gmac_no);
if (gmac_no > 2 && gmac_no < 6)
ipq40xx_patch_eth_params(blob, gmac_no);
}
dcache_disable();
#ifdef CONFIG_IPQ_FDT_FIXUP
ipq_fdt_fixup(blob);
#endif
fdt_fixup_ethernet(blob);
ipq_fdt_fixup_usb_device_mode(blob);
fdt_fixup_auto_restart(blob);
fdt_fixup_sd_ldo_gpios_toggle(blob);
fdt_fixup_cpr(blob);
fdt_fixup_cpus_node(blob);
fdt_low_memory_fixup(blob);
fdt_fixup_qpic(blob);
s = getenv("dload_warm_reset");
if (s)
fdt_fixup_set_dload_warm_reset(blob);
s = getenv("dload_dis");
if (s)
ipq_fdt_mem_rsvd_fixup(blob);
s = getenv("qce_fixed_key");
if (s)
fdt_fixup_set_qce_fixed_key(blob);
s = getenv("atf");
if (s) {
fdt_fixup_set_qca_cold_reboot_enable(blob);
fdt_fixup_wcss_rproc_for_atf(blob);
}
#ifdef CONFIG_IPQ_BT_SUPPORT
fdt_fixup_bt_running(blob);
#endif
/*
|| This features fixup compressed_art in
|| dts if its 16M profile build.
*/
fdt_fixup_art_format(blob);
#ifdef CONFIG_QCA_MMC
board_mmc_deinit();
#endif
return 0;
}
#endif /* CONFIG_OF_BOARD_SETUP */