blob: b17c4d5b3185f3a33d955d4e89742be34d451e3e [file] [log] [blame]
/*******************************************************************************
Copyright (C) Marvell International Ltd. and its affiliates
********************************************************************************
Marvell GPL License Option
If you received this File from Marvell, you may opt to use, redistribute and/or
modify this File in accordance with the terms and conditions of the General
Public License Version 2, June 1991 (the "GPL License"), a copy of which is
available along with the File in the license.txt file or by writing to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
DISCLAIMED. The GPL License provides additional details about this warranty
disclaimer.
*******************************************************************************/
#include <common.h>
#include <command.h>
#include "mvOs.h"
#include "mv_amp.h"
#include "mvCpuIfRegs.h"
#include "uart/mvUart.h"
#include "libfdt.h"
#include "fdt_support.h"
#include "mvBoardEnvLib.h"
#include "ethSwitch/mvSwitchRegs.h"
#include "ethSwitch/mvSwitch.h"
/*******************************************************************************
* fdt_env_setup
*
* DESCRIPTION:
*
* INPUT:
* fdtfile.
*
* OUTPUT:
* None.
*
* RETURN:
* None.
*
*******************************************************************************/
void fdt_env_setup(char *fdtfile, MV_BOOL runUpdate)
{
#if CONFIG_OF_LIBFDT
char *env;
env = getenv("fdt_skip_update"); /* if set to yes, automatic board setup will be skipped */
if (!env)
setenv("fdt_skip_update", runUpdate ? "no" : "yes");
env = getenv("fdtaddr");
if (!env)
setenv("fdtaddr", "0x1000000");
env = getenv("fdtfile");
if (!env)
setenv("fdtfile", fdtfile);
/* boot command to fetch DT file, update DT (if fdt_skip_update=no) and bootz LSP zImage */
char bootcmd_fdt[] = "tftpboot 0x2000000 $image_name;tftpboot $fdtaddr $fdtfile;"
"setenv bootargs $console $nandEcc $mtdparts $bootargs_root nfsroot=$serverip:$rootpath "
"ip=$ipaddr:$serverip$bootargs_end $mvNetConfig video=dovefb:lcd0:$lcd0_params "
"clcd.lcd0_enable=$lcd0_enable clcd.lcd_panel=$lcd_panel; bootz 0x2000000 - $fdtaddr;";
/* boot command to and bootz LSP zImage (after DT already fetch and set) */
env = getenv("bootcmd_fdt_boot");
if (!env)
setenv("bootcmd_fdt_boot", "tftpboot 0x2000000 $image_name; setenv bootargs $console $nandEcc "
"$mtdparts $bootargs_root nfsroot=$serverip:$rootpath ip=$ipaddr:$serverip$bootargs_end "
"$mvNetConfig video=dovefb:lcd0:$lcd0_params clcd.lcd0_enable=$lcd0_enable "
"clcd.lcd_panel=$lcd_panel; bootz 0x2000000 - $fdtaddr;");
/* boot command to fetch DT file, and set bootcmd_fdt_boot as new bootcmd (manually edit DT & run 'boot') */
env = getenv("bootcmd_fdt_edit");
if (!env)
setenv("bootcmd_fdt_edit", "tftpboot $fdtaddr $fdtfile; fdt addr $fdtaddr; setenv bootcmd $bootcmd_fdt_boot");
env = getenv("bootcmd_fdt");
if (!env)
setenv("bootcmd_fdt",bootcmd_fdt);
#if defined (CONFIG_OF_LIBFDT_IS_DEFAULT)
env = getenv("bootcmd");
if (!env)
setenv("bootcmd", bootcmd_fdt);
#endif /* (CONFIG_OF_LIBFDT_IS_DEFAULT) */
#endif
}
#ifdef CONFIG_OF_BOARD_SETUP
#define MV_FDT_MAXDEPTH 32
static int mv_fdt_find_node(void *fdt, const char *name);
static int mv_fdt_board_compatible_name_update(void *fdt);
static int mv_fdt_update_serial(void *fdt);
#ifdef CONFIG_PIC_GPIO
static int mv_fdt_update_pic_gpio(void *fdt);
#endif
static int mv_fdt_update_cpus(void *fdt);
static int mv_fdt_update_pex(void *fdt);
#ifdef MV_INCLUDE_SATA
static int mv_fdt_update_sata(void *fdt);
#endif
#ifdef MV_INCLUDE_USB
static int mv_fdt_update_usb(void *fdt, MV_UNIT_ID unitType);
static int mv_fdt_update_usb2(void *fdt);
static int mv_fdt_update_usb3(void *fdt);
#endif
static int mv_fdt_update_ethnum(void *fdt);
static int mv_fdt_update_flash(void *fdt);
static int mv_fdt_set_node_prop(void *fdt, const char *node, const char *prop, const char *prop_val);
static int mv_fdt_remove_node(void *fdt, const char *path);
static int mv_fdt_scan_and_set_alias(void *fdt, const char *name, const char *alias);
static int mv_fdt_nand_mode_fixup(void *fdt);
static int mv_fdt_update_pinctrl(void *fdt);
#ifdef MV_INCLUDE_AUDIO
static int mv_fdt_update_audio(void *fdt);
#endif
#ifdef MV_INCLUDE_SWITCH
static int mv_fdt_update_switch(void *fdt);
#endif
static int mv_fdt_debug;
#ifdef CONFIG_MV_SDHCI
static int mv_fdt_update_sdhci(void *fdt);
#endif
#ifdef CONFIG_SWITCHING_SERVICES
static int mv_fdt_update_prestera(void *fdt);
#endif
#ifdef MV_INCLUDE_TDM
static int mv_fdt_update_tdm(void *fdt);
#endif
#ifdef MV_USB_VBUS_CYCLE
static int mv_fdt_update_usb_vbus(void *fdt);
#endif
#ifdef MV_INCLUDE_USB_DEVICE
static int mv_fdt_update_usb_device(void *fdt);
#endif
#if 0 /* not compiled, since this routine is currently not in use */
static int mv_fdt_remove_prop(void *fdt, const char *path,
const char *name, int nodeoff);
#endif
typedef int (update_fnc_t)(void *);
update_fnc_t *update_sequence[] = {
mv_fdt_update_cpus, /* Get number of CPUs and update dt */
mv_fdt_update_pex, /* Get number of active PEX port and update DT */
#ifdef MV_INCLUDE_SATA
mv_fdt_update_sata, /* Get number of active SATA units and update DT */
#endif
#ifdef MV_INCLUDE_USB
mv_fdt_update_usb2, /* Get number of active USB2.0 units and update DT */
mv_fdt_update_usb3, /* Get number of active USB3.0 units and update DT */
#endif
mv_fdt_update_ethnum, /* Get number of active ETH port and update DT */
#ifdef CONFIG_MV_SDHCI
mv_fdt_update_sdhci, /* Update SDHCI driver settings in DT */
#endif
#ifdef CONFIG_SWITCHING_SERVICES
mv_fdt_update_prestera, /* Update prestera driver DT settings - for AMC board */
#endif
#ifdef MV_INCLUDE_TDM
mv_fdt_update_tdm, /* Update tdm node in DT */
#endif
#ifdef MV_USB_VBUS_CYCLE
mv_fdt_update_usb_vbus,
#endif
mv_fdt_update_flash, /* Get number of active flash devices and update DT */
mv_fdt_nand_mode_fixup, /* Update NAND controller ECC settings in DT */
mv_fdt_update_pinctrl, /* Update pinctrl driver settings in DT */
mv_fdt_board_compatible_name_update, /* Update compatible (board name) in DT */
mv_fdt_update_serial, /* Update serial/UART nodes in DT */
#ifdef CONFIG_PIC_GPIO
mv_fdt_update_pic_gpio,
#endif
#ifdef MV_INCLUDE_AUDIO
mv_fdt_update_audio, /* Update audio-controller+sound nodes in DT */
#endif
#ifdef MV_INCLUDE_SWITCH
mv_fdt_update_switch,
#endif
#ifdef MV_INCLUDE_USB_DEVICE
mv_fdt_update_usb_device,
#endif
NULL,
};
enum nfc_driver_type {
MV_FDT_NFC_PXA3XX, /* mainline pxa3xx-nand */
MV_FDT_NFC_HAL, /* mvebu HAL-based NFC */
MV_FDT_NFC_HAL_A375, /* mvebu HAL-based NFC for A375 */
MV_FDT_NFC_NONE,
};
#define mv_fdt_dprintf(...) \
if (mv_fdt_debug) \
printf(__VA_ARGS__);
#define mv_fdt_modify(fdt, err, function) \
do { \
err = function; /* Try requested routine */ \
while (err == -FDT_ERR_NOSPACE) { \
mv_fdt_dprintf("Resize fdt...\n"); \
fdt_resize(fdt); \
err = function; /* Retry requested routine*/ \
} \
} while (0)
/*******************************************************************************
* ft_board_setup
*
* DESCRIPTION:
*
* INPUT:
* blob.
* bd.
*
* OUTPUT:
* None.
*
* RETURN:
* None.
*
*******************************************************************************/
void ft_board_setup(void *blob, bd_t *bd)
{
int err; /* error number */
char *env; /* env value */
update_fnc_t **update_fnc_ptr;
/* Debug information will be printed if variable enaFDTdebug=yes */
env = getenv("enaFDTdebug");
if (env && ((strncmp(env, "yes", 3) == 0)))
mv_fdt_debug = 1;
else
mv_fdt_debug = 0;
/* Update dt information for all SoCs */
/* Update dt bootargs from commandline */
fdt_resize(blob);
mv_fdt_modify(blob, err, fdt_chosen(blob, 1));
if (err < 0) {
mv_fdt_dprintf("Updating DT bootargs failed\n");
goto bs_fail;
}
mv_fdt_dprintf("DT bootargs updated from commandline\n");
/* Update ethernet aliases with nodes' names and update mac addresses */
err = mv_fdt_scan_and_set_alias(blob, "ethernet@", "ethernet");
if (err < 0)
goto bs_fail;
fdt_fixup_ethernet(blob);
mv_fdt_dprintf("DT ethernet MAC addresses updated from environment\n");
/* Update memory node */
fixup_memory_node(blob);
mv_fdt_dprintf("Memory node updated\n");
/* Make updates of functions in update_sequence */
for (update_fnc_ptr = update_sequence; *update_fnc_ptr; ++update_fnc_ptr) {
err = (*update_fnc_ptr)(blob);
if (err < 0)
goto bs_fail;
}
printf("Updating device tree successful\n");
return;
bs_fail:
printf("Updating device tree failed\n");
return;
}
/*******************************************************************************
* mv_fdt_find_node
*
* DESCRIPTION:
*
* INPUT:
* fdt.
* name.
*
* OUTPUT:
* None.
*
* RETURN:
* node offset or -1 if node not found.
*
*******************************************************************************/
static int mv_fdt_find_node(void *fdt, const char *name)
{
int nodeoffset; /* current node's offset from libfdt */
int nextoffset; /* next node's offset from libfdt */
int level = 0; /* current fdt scanning depth */
uint32_t tag; /* device tree tag at given offset */
const char *node; /* node name */
/* Set scanning starting point to '/' */
nodeoffset = fdt_path_offset(fdt, "/");
if (nodeoffset < 0) {
mv_fdt_dprintf("%s: failed to get '/' nodeoffset\n",
__func__);
return -1;
}
/* Scan device tree for node = 'name' and return its offset */
while (level >= 0) {
tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
switch (tag) {
case FDT_BEGIN_NODE:
node = fdt_get_name(fdt, nodeoffset, NULL);
if (strncmp(node, name, strlen(name)+1) == 0)
return nodeoffset;
level++;
if (level >= MV_FDT_MAXDEPTH) {
mv_fdt_dprintf("%s: Nested too deep, "
"aborting.\n", __func__);
return -1;
}
break;
case FDT_END_NODE:
level--;
if (level == 0)
level = -1; /* exit the loop */
break;
case FDT_PROP:
case FDT_NOP:
break;
case FDT_END:
mv_fdt_dprintf("Device tree scanning failed - end of "
"tree tag\n");
return -1;
default:
mv_fdt_dprintf("Device tree scanning failed - unknown "
"tag\n");
return -1;
}
nodeoffset = nextoffset;
}
/* Node not found in the device tree */
return -1;
}
/*******************************************************************************
* mv_fdt_update_cpus
*
* DESCRIPTION:
* target : remove excessice cpu nodes.
* node : cpus
* dependencies : CPU core num in SOC_COHERENCY_FABRIC_CFG_REG.
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_cpus(void *fdt)
{
int cpusnodes = 0; /* number of cpu nodes */
int nodeoffset; /* node offset from libfdt */
int err; /* error number */
const char *node; /* node name */
char *p; /* auxiliary pointer */
char *lastcpu; /* pointer to last valid cpu */
MV_U8 cpusnum; /* number of cpus */
int depth = 2;
/* Get CPUs number and remove unnecessary nodes */
cpusnum = mvCtrlGetCpuNum() + 1;
mv_fdt_dprintf("Number of CPUs detected: %d\n", cpusnum);
/* Find cpus node */
node = "cpus";
nodeoffset = mv_fdt_find_node(fdt, node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", node);
return -1;
}
p = malloc(strlen("cpu@x")+1);
lastcpu = malloc(strlen("cpu@x")+1);
while (strncmp(node, "cpu", 3) == 0) {
cpusnodes++;
/* Remove excessive cpu nodes */
if (cpusnodes > cpusnum + 1) {
strcpy(p, node);
err = mv_fdt_remove_node(fdt, (const char *)p);
if (err < 0) {
mv_fdt_dprintf("Failed to remove %s\n",
node);
free(p);
free(lastcpu);
return -1;
}
node = lastcpu;
nodeoffset = mv_fdt_find_node(fdt, node);
} else {
strcpy(lastcpu, node);
}
nodeoffset = fdt_next_node(fdt, nodeoffset, &depth);
node = fdt_get_name(fdt, nodeoffset, NULL);
}
free(p);
free(lastcpu);
return 0;
}
#ifdef MV_INCLUDE_SATA
/*******************************************************************************
* mv_fdt_update_sata
*
* DESCRIPTION:
* target : Update status field of SATA nodes.
* node, properties : -property status @ node SATA
* dependencies : sata unit status in sataUnitActive array.
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_sata(void *fdt)
{
int i;
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
char node[64]; /* node name */
MV_U32 sataUnitNum = mvCtrlSataMaxUnitGet(); /* number of interfaces */
mv_fdt_dprintf("Maximum SATA Units = %d\n, Active SATA Units:", sataUnitNum);
for (i = 0; i < sataUnitNum; i++) {
if (mvCtrlIsActiveSataUnit(i))
sprintf(propval, "okay"); /* Enable active SATA interfaces */
else
sprintf(propval, "disabled"); /* disable NON active SATA units */
sprintf(node, "sata@%x", mvCtrlSataRegBaseGet(i));
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
}
return 0;
}
#endif /* MV_INCLUDE_SATA */
#ifdef MV_INCLUDE_TDM
/*******************************************************************************
* mv_fdt_update_tdm
*
* DESCRIPTION:
* target : update status field of tdm@X node.
* node, properties : -property status @ node tdm@x.
* dependencies : isTdmConnected entry in board structure.
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_tdm(void *fdt)
{
int err, nodeoffset; /* nodeoffset: node offset from libfdt */
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
char node[64]; /* node name */
if (mvBoardIsTdmConnected() == MV_TRUE)
sprintf(propval, "okay");
else
sprintf(propval, "disabled");
sprintf(node, "tdm@%x", MV_TDM_OFFSET);
nodeoffset = mv_fdt_find_node(fdt, node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", node);
return 0;
}
if (strncmp(fdt_get_name(fdt, nodeoffset, NULL), node, strlen(node)) == 0) {
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop,
propval, strlen(propval)+1));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, node);
return 0;
}
mv_fdt_dprintf("Set '%s' property to '%s' in '%s' node\n", prop, propval, node);
}
return 0;
}
#endif
#ifdef MV_USB_VBUS_CYCLE
/*******************************************************************************
* mv_fdt_update_usb_vbus
*
* DESCRIPTION:
* target : update status field of usb3_phy and usb3-vbus-gpio/usb3-vbus-exp1 node.
* node, properties : -property status @ node usb3_phy and usb3-vbus-gpio/usb3-vbus-exp1 node.
* dependencies : BOARD_GPP_USB_VBUS entry in BoardGppInfo --> usb3-vbus-gpio node
* MV_IO_EXPANDER_USB_VBUS entry in BoardIoExpPinInfo --> usb3-vbus-exp1
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_usb_vbus(void *fdt)
{
int err, nodeoffset; /* nodeoffset: node offset from libfdt */
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
const char *node = NULL, *phy_node = NULL; /* node name */
MV_BOARD_IO_EXPANDER_TYPE_INFO ioInfo; /* empty stub - not to be used */
sprintf(propval, "okay");
if (mvBoardIoExpanderTypeGet(MV_IO_EXPANDER_USB_VBUS, &ioInfo) != MV_ERROR) {
node = "usb3-vbus-exp1";
phy_node = "usb3-phy-exp1";
} else {
/* remove invalid i2c pins information, to avoid unexpected Linux behaviour*/
mv_fdt_remove_node(fdt, "i2c-pins-0");
}
if (mvBoardUSBVbusGpioPinGet(0) != MV_ERROR) {
node = "usb3-vbus-gpio";
phy_node = "usb3-phy-gpio";
} else {
/* remove invalid vbus gpio pins information, to avoid unexpected Linux behaviour*/
mv_fdt_remove_node(fdt, "xhci0-vbus-pins");
}
if (!node && !phy_node)
return 0;
nodeoffset = mv_fdt_find_node(fdt, node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", node);
return 0;
}
if (strncmp(fdt_get_name(fdt, nodeoffset, NULL), node, strlen(node)) == 0) {
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop,
propval, strlen(propval)+1));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, node);
return 0;
}
mv_fdt_dprintf("Set '%s' property to '%s' in '%s' node\n", prop, propval, node);
}
/* enable 'usb3_phy' node - for Linux regulator usage */
nodeoffset = mv_fdt_find_node(fdt, phy_node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", phy_node);
return 0;
}
if (strncmp(fdt_get_name(fdt, nodeoffset, NULL), phy_node, strlen(phy_node)) == 0) {
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop,
propval, strlen(propval)+1));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, phy_node);
return 0;
}
mv_fdt_dprintf("Set '%s' property to '%s' in '%s' node\n", prop, propval, phy_node);
} return 0;
return 0;
}
#endif /* MV_USB_VBUS_CYCLE */
#ifdef CONFIG_MV_SDHCI
/*******************************************************************************
* mv_fdt_update_sdhci
*
* DESCRIPTION:
* target : update status and dat3-cd fields of MMC node.
* node, properties : -property status and dat3-cd @ node sdhci@X.
* dependencies : status of MMC in isSdMmcConnected entry in board structure.
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_sdhci(void *fdt)
{
int err, nodeoffset; /* nodeoffset: node offset from libfdt */
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
char node[64]; /* node name */
char *env;
if (mvBoardisSdioConnected())
sprintf(propval, "okay");
else
sprintf(propval, "disabled");
sprintf(node, "sdhci@%x", MV_SDMMC_REGS_OFFSET);
nodeoffset = mv_fdt_find_node(fdt, node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", node);
return 0;
}
if (strncmp(fdt_get_name(fdt, nodeoffset, NULL), node, strlen(node)) == 0) {
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop,
propval, strlen(propval)+1));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, node);
return 0;
}
mv_fdt_dprintf("Set '%s' property to '%s' in '%s' node\n", prop, propval, node);
}
/* add DAT3 detection */
env = getenv("sd_detection_dat3");
if (!env || strcmp(env, "yes") != 0)
return 0;
prop = "dat3-cd";
/* add empty property 'dat3-cd' */
if (fdt_appendprop(fdt, nodeoffset, prop, NULL, 0) < 0)
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
#endif
#ifdef MV_INCLUDE_AUDIO
/*******************************************************************************
* mv_fdt_update_audio
*
* DESCRIPTION: update audio-controller and sound nodes (SPDIF/I2S)
*
* INPUT: fdt.
* OUTPUT: None.
* RETURN: -1 on error os 0 otherwise.
*******************************************************************************/
static int mv_fdt_update_audio(void *fdt)
{
int err, nodeoffset, i; /* nodeoffset: node offset from libfdt */
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
char nodes[2][64]; /* node names */
if (mvBoardIsAudioConnected() == MV_TRUE)
sprintf(propval, "okay");
else
sprintf(propval, "disabled");
sprintf(nodes[0], "audio-controller@%x", MV_AUDIO_OFFSET);
sprintf(nodes[1], "sound");
for (i = 0; i < 2; ++i) {
nodeoffset = mv_fdt_find_node(fdt, nodes[i]);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", nodes[i]);
return 0;
}
if (strncmp(fdt_get_name(fdt, nodeoffset, NULL), nodes[i], strlen(nodes[i])) == 0) {
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop,
propval, strlen(propval)+1));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, nodes[i]);
return 0;
}
mv_fdt_dprintf("Set '%s' property to '%s' in '%s' node\n", prop, propval, nodes[i]);
}
}
return 0;
}
#endif
#ifdef CONFIG_SWITCHING_SERVICES
/*******************************************************************************
* mv_fdt_update_prestera
*
* DESCRIPTION:
* target : update status field of prestera node by checking if the board is AMC.
* node, properties : -property status @ node prestera.
* dependencies : AMC status saved in isAmc entry in board structure
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_prestera(void *fdt)
{
int err, nodeoffset; /* nodeoffset: node offset from libfdt */
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
const char *node = "prestera"; /* node name */
MV_BOOL isAmc;
isAmc = mvBoardisAmc();
if (isAmc)
sprintf(propval, "okay");
else
sprintf(propval, "disabled");
nodeoffset = mv_fdt_find_node(fdt, node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", node);
return 0;
}
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop,
propval, strlen(propval)+1));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, node);
return -1;
}
mv_fdt_dprintf("Set '%s' property to '%s' in '%s' node\n", prop, propval, node);
return 0;
}
#endif
/*******************************************************************************
* mv_fdt_update_pex
*
* DESCRIPTION:
* target : update status fields of pcieX nodes.
* node, properties : -property status @ node pcieX.
* dependencies : boardPexInfo structure entry in board structure.
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_pex(void *fdt)
{
MV_U32 pexnum; /* number of interfaces */
MV_BOARD_PEX_INFO *boardPexInfo; /* pex info */
int err; /* error number */
int nodeoffset; /* node offset from libfdt */
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
const char *node = "pcie-controller"; /* node name */
int i = 0;
int k = 0;
int depth = 1;
/* Get number of active pex interfaces */
boardPexInfo = mvBoardPexInfoGet();
pexnum = boardPexInfo->boardPexIfNum;
mv_fdt_dprintf("Number of active PEX ports detected = %d\n", pexnum);
mv_fdt_dprintf("Active PEX HW interfaces: ");
for (k = 0; k < pexnum; k++)
mv_fdt_dprintf("%d, ", boardPexInfo->pexMapping[k]);
mv_fdt_dprintf("\n");
/* Set controller and 'pexnum' number of interfaces' status to 'okay'.
* Rest of them are disabled */
nodeoffset = mv_fdt_find_node(fdt, node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", node);
return 0;
}
while (strncmp(node, "pcie", 4) == 0) {
for (k = 0; k <= pexnum; k++)
if (i == boardPexInfo->pexMapping[k]) {
sprintf(propval, "okay");
goto pex_ok;
}
sprintf(propval, "disabled");
pex_ok:
if (strncmp(node, "pcie-controller", 15) != 0)
i++;
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop,
propval, strlen(propval)+1));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n",
prop, node);
return 0;
}
mv_fdt_dprintf("Set '%s' property to '%s' in '%s' node\n", prop,
propval, node);
nodeoffset = fdt_next_node(fdt, nodeoffset, &depth);
if (nodeoffset < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n",
prop, node);
return 0;
}
node = fdt_get_name(fdt, nodeoffset, NULL);
}
return 0;
}
#ifdef MV_INCLUDE_USB
/*******************************************************************************
* mv_fdt_update_usb
*
* DESCRIPTION: update USB2.0(eHCI) & USB3.0(xHCI) status
*
* INPUT: fdt.
* OUTPUT: None.
* RETURN: -1 on error os 0 otherwise.
*******************************************************************************/
static int mv_fdt_update_usb(void *fdt, MV_UNIT_ID unitType)
{
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
char node[64]; /* node name */
int i, maxUsbPorts = unitType == USB_UNIT_ID ? MV_USB_MAX_PORTS : MV_USB3_MAX_HOST_PORTS;
/* update USB2.0 ports status */
for (i = 0; i < maxUsbPorts; i++) {
if (mvBoardIsUsbPortConnected(unitType, i))
sprintf(propval, "okay"); /* Enable active SATA interfaces */
else
sprintf(propval, "disabled"); /* disable NON active SATA units */
sprintf(node, "usb%s@%x", unitType == USB_UNIT_ID ? "" : "3", MV_USB2_USB3_REGS_OFFSET(unitType, i));
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
}
return 0;
}
/*******************************************************************************
* mv_fdt_update_usb2
*
* DESCRIPTION: update USB2.0(eHCI) status, this function is wrapper function
* to mv_fdt_update_usb, In order to add it to update_sequence array
*
* INPUT: fdt.
* OUTPUT: None.
* RETURN: -1 on error os 0 otherwise.
*******************************************************************************/
static int mv_fdt_update_usb2(void *fdt)
{
return mv_fdt_update_usb(fdt, USB_UNIT_ID);
}
/*******************************************************************************
* mv_fdt_update_usb3
*
* DESCRIPTION: update USB3.0(xHCI) status, this function is wrapper function
* to mv_fdt_update_usb, In order to add it to update_sequence array
*
* INPUT: fdt.
* OUTPUT: None.
* RETURN: -1 on error os 0 otherwise.
*******************************************************************************/
static int mv_fdt_update_usb3(void *fdt)
{
return mv_fdt_update_usb(fdt, USB3_UNIT_ID);
}
#endif /* MV_INCLUDE_USB */
#ifdef MV_INCLUDE_USB_DEVICE
/*******************************************************************************
* mv_fdt_update_usb_device
*
* DESCRIPTION: usb3 device status
* target : update status field of u3d and udc nodes.
* node, properties : property status @ nodes usb3@X, udc@X and udc@X.
* dependencies : S@R field 'usb3port0' or 'usb3port1'.
*
* INPUT: fdt.
* OUTPUT: None.
* RETURN: -1 on error os 0 otherwise.
*******************************************************************************/
static int mv_fdt_update_usb_device(void *fdt)
{
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
char node[64]; /* node name */
int i;
MV_BOOL isDevice = MV_FALSE;
/* disable usb3 nodes */
for (i = 0; i < MV_USB3_MAX_HOST_PORTS; i++) {
if (mvBoardIsUsb3PortDevice(i)) {
/* if Port is Device, disable corresponding Host node */
sprintf(propval, "disabled");
isDevice = MV_TRUE;
break;
}
}
if (isDevice == MV_FALSE)
return 0;
sprintf(node, "usb3@%x", MV_USB2_USB3_REGS_OFFSET(USB3_UNIT_ID, i));
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0)
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
/* enable 'u3d' and 'udc' nodes */
sprintf(propval, "okay");
sprintf(node, "u3d@%x", MV_USB3_DEVICE_REGS_OFFSET);
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
sprintf(node, "udc@%x", MV_USB3_DEVICE_USB2_REGS_OFFSET);
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
return 0;
}
#endif /* MV_INCLUDE_USB_DEVICE */
/*******************************************************************************
* mv_fdt_update_pinctrl
*
* DESCRIPTION:
* target : update compatible field of pinctrl node.
* node, properties : -property compatible @ node pinctrl.
* dependencies :DEV_ID_REG value "device model type".
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_pinctrl(void *fdt)
{
char propval[128]; /* property value */
const char *prop = "compatible"; /* property name */
const char *node = "pinctrl"; /* node name */
/* update pinctrl driver 'compatible' property, according to device model type */
sprintf(propval, "marvell,mv88f%x-pinctrl", mvCtrlModelGet());
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
return 0;
}
/*******************************************************************************
* mv_fdt_update_ethnum
*
* DESCRIPTION:
* target : update ethernet nodes for PP3/Neta drivers.
* node, properties : PP3 Driver : -property status @ node nic@X.
* -property phy-mode and phy-id @ node macX.
* Neta Driver : -property status and phy-mode @ node ethernet@X.
* -property reg @ node ethernet-phy@X.
* dependencies : pBoardNetComplexInfo entry in board structure.
* pBoardMacInfo entry in board structure.
* For PP3/Neta driver we get ethernet nodes via "nic@/ethernet@" aliasses.
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_ethnum(void *fdt)
{
MV_32 phyAddr;
int port;
int len = 0; /* property length */
int ethPortsNum; /* nodes' counter */
int nodeoffset; /* node offset from libfdt */
int aliasesoffset; /* aliases node offset from libfdt */
int phyoffset;
const uint32_t *phandle;
char prop[20]; /* property name */
char propval[20]; /* property value */
const char *node; /* node name */
int depth = 1;
int err;
/* Get number of ethernet nodes */
aliasesoffset = mv_fdt_find_node(fdt, "aliases");
if (aliasesoffset < 0) {
mv_fdt_dprintf("Lack of 'aliases' node in device tree\n");
return 0;
}
/* Walk trough all aliases and count Ethernet ports entries */
ethPortsNum = 0;
nodeoffset = aliasesoffset;
do {
nodeoffset = fdt_next_node(fdt, nodeoffset, &depth);
node = fdt_get_name(fdt, nodeoffset, NULL);
if (strncmp(node, "ethernet@", 9) == 0) /* Node name can be NULL, but it's OK */
ethPortsNum++;
} while (node != NULL);
mv_fdt_dprintf("Number of ethernet nodes in DT = %d\n", ethPortsNum);
/* Get path to ethernet node from property value */
for (port = 0; port < ethPortsNum; port++) {
/* Get path to ethernet node from property value */
sprintf(prop, "ethernet%d", port);
node = fdt_getprop(fdt, aliasesoffset, prop, &len);
if (node == NULL) {
mv_fdt_dprintf("Lack of '%s' property in 'aliases' node\n", prop);
return 0;
}
if (len == 0) {
mv_fdt_dprintf("Empty property value\n");
return 0;
}
/* Alias points to the ETH port node using full DT path */
nodeoffset = fdt_path_offset(fdt, node);
/* Set ethernet port status to 'disabled' */
/* Enable valid ports and configure their parametrs, disable non valid ones */
if (mvBoardIsEthConnected(port) != MV_TRUE)
sprintf(propval, "disabled");
else {
/* Configure PHY address */
phyAddr = mvBoardPhyAddrGet(port);
if (phyAddr == -1)
phyAddr = 999; /* Inband management - see mv_netdev.c for details */
/* The ETH node we found contains a pointer (phandle) to its PHY
The phandle is a unique numeric identifier of a specific node */
phandle = fdt_getprop(fdt, nodeoffset, "phy", &len);
if (len == 0) {
mv_fdt_dprintf("Empty \"phy\" property value\n");
return 0;
}
phyoffset = fdt_node_offset_by_phandle(fdt, ntohl(*phandle));
if (phyoffset < 0) {
mv_fdt_dprintf("Failed to get PHY node by phandle %x\n", ntohl(*phandle));
return 0;
}
/* Setup PHY address in DT in "reg" property of an appropriate PHY node.
This value is HEX number, not a string, and uses network byte order */
phyAddr = htonl(phyAddr);
sprintf(prop, "reg");
mv_fdt_modify(fdt, err, fdt_setprop(fdt, phyoffset, prop, &phyAddr, sizeof(phyAddr)));
if (err < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n",
prop, fdt_get_name(fdt, phyoffset, NULL));
return 0;
}
mv_fdt_dprintf("Set property '%s' of node '%s' to %#010x\n",
prop, fdt_get_name(fdt, phyoffset, NULL), ntohl(phyAddr));
/* Configure PHY mode */
switch (mvBoardPortTypeGet(port)) {
case MV_PORT_TYPE_SGMII: /* regardless the fact that qsgmii is supported by kernel DT,*/
case MV_PORT_TYPE_QSGMII:/* the ETH driver does not make use of this connection mode */
sprintf(propval, "sgmii");
break;
case MV_PORT_TYPE_RGMII:
sprintf(propval, "rgmii");
break;
default:
mv_fdt_dprintf("Bad port type received for interface %d\n", port);
return 0;
}
/* Setup PHY connection type in DT */
sprintf(prop, "phy-mode");
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop, propval, strlen(propval)+1));
if (err < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
mv_fdt_dprintf("Set '%s' property to '%s' in '%s' node\n", prop, propval, node);
/* Last property to set is the "status" - common for valid and non-valid ports */
sprintf(propval, "okay");
}
sprintf(prop, "status");
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop, propval, strlen(propval)+1));
if (err < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
mv_fdt_dprintf("Set '%s' property to '%s' in '%s' node\n", prop, propval, node);
} /* For all ports in board sctructure */
return 0;
}
/*******************************************************************************
* mv_fdt_update_flash
*
* DESCRIPTION:
* Update FDT entires related to board flash devices according to board sctructures.
* The board sctructures are scanned and initialized at uboot startup using board
* boot source and bord type. This function obtails flash entries marked as active
* during the board initalization process and activates appropriate nodes in FDT.
* Nodes corresponding to non-active flash devices are disabled in FDT.
*
* INPUT:
* fdt - FDT
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_flash(void *fdt)
{
MV_U32 device;
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
char node[64]; /* node name */
MV_U32 flashNum; /* number of flashes defined for beard */
MV_U32 unit, maxUnits; /* number of interface controller units */
MV_U32 chipSel;
MV_BOOL interfaceIsActive;
/* start with SPI flashes */
flashNum = mvBoardGetDevicesNumber(BOARD_DEV_SPI_FLASH);
maxUnits = mvCtrlSocUnitInfoNumGet(SPI_UNIT_ID);
for (unit = 0; unit < maxUnits; unit++) {
interfaceIsActive = MV_FALSE;
for (device = 0; device < flashNum; device++) {
/* Only devices related to current bus/unit */
if (mvBoardGetDevBusNum(device, BOARD_DEV_SPI_FLASH) != unit)
continue;
if (mvBoardGetDevState(device, BOARD_DEV_SPI_FLASH) == MV_TRUE) {
interfaceIsActive = MV_TRUE; /* One active device is enough */
break;
}
} /* SPI devices */
if (interfaceIsActive == MV_TRUE)
sprintf(propval, "okay"); /* Enable active SPI unit/bus */
else
sprintf(propval, "disabled"); /* disable NON active SPI unit/bus */
sprintf(node, "spi@%x", MV_SPI_REGS_OFFSET(unit));
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
} /* SPI units/buses */
/* handle NAND flashes - there is only one NAND unit, but different CS are possible */
flashNum = mvBoardGetDevicesNumber(BOARD_DEV_NAND_FLASH);
interfaceIsActive = MV_FALSE;
for (device = 0; device < flashNum; device++) {
if (mvBoardGetDevState(device, BOARD_DEV_NAND_FLASH) == MV_TRUE) {
interfaceIsActive = MV_TRUE; /* One active device is enough */
/* Once a NAND node updated, there is no reason to search for other devices */
break;
}
} /* NAND devices */
if (interfaceIsActive == MV_TRUE)
sprintf(propval, "okay"); /* Enable NAND interface if found active device */
else
sprintf(propval, "disabled"); /* disable NAND interface if NOT found active devices */
sprintf(node, "nand@%x", MV_NFC_REGS_OFFSET);
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
/* handle NOR flashes - there is only one NOR unit, but different CS are possible */
flashNum = mvBoardGetDevicesNumber(BOARD_DEV_NOR_FLASH);
chipSel = 0xFFFF;
for (device = 0; device < flashNum; device++) {
if (mvBoardGetDevState(device, BOARD_DEV_NOR_FLASH) != MV_TRUE)
continue;
chipSel = mvBoardGetDevCSNum(device, BOARD_DEV_NOR_FLASH);
if (chipSel == DEV_BOOCS) /* Special case - this value is not close to the rest of Device Bus */
chipSel = 4; /* Chip Selects (0 to 3) in target definitiond enum, */
else /* so it will be hadled just as a 4th chip select */
chipSel -= DEVICE_CS0; /* Substract the base value for getting 0-3 CS range */
/* Once an active NOR flash entry found, there is no reason to search for others */
break;
} /* NOR devices */
/* Walk through Device Bus entries and update them */
for (device = 0; device < 5; device++) {
if (device == chipSel)
sprintf(propval, "okay"); /* Enable NOR interface if found active device */
else
sprintf(propval, "disabled"); /* disable NOR interface if NOT found active devices */
if (device == 4) /* Fourth CS is a Boot CS - see the comment above */
sprintf(node, "devbus-bootcs");
else
sprintf(node, "devbus-cs%d", device);
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
}
return 0;
}
/*******************************************************************************
* mv_fdt_set_node_prop
*
* DESCRIPTION:
* Set a named node property to a specific value
*
* INPUT:
* fdt - FDT
* node - FDT node name
* prop - FDT node property name
* propval - property value to set
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_set_node_prop(void *fdt, const char *node, const char *prop, const char *propval)
{
int err, nodeoffset = 0; /* nodeoffset: node offset from libfdt */
if (node != NULL) { /* node == NULL --> search property in root node */
nodeoffset = mv_fdt_find_node(fdt, node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", node);
return -1;
}
}
if (strncmp(fdt_get_name(fdt, nodeoffset, NULL), node, strlen(node)) == 0) {
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop, propval, strlen(propval)+1));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, node);
return -1;
}
}
mv_fdt_dprintf("Set '%s' property to '%s' in '%s' node\n", prop, propval, node);
return 0;
}
#if 0 /* not compiled, since this routine is currently not in use */
/*******************************************************************************
* mv_fdt_remove_prop
*
* DESCRIPTION:
*
* INPUT:
* fdt.
* path.
* name.
* nodeoff.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_remove_prop(void *fdt, const char *path,
const char *name, int nodeoff)
{
int error;
error = fdt_delprop(fdt, nodeoff, name);
if (error == -FDT_ERR_NOTFOUND) {
mv_fdt_dprintf("'%s' already removed from '%s' node\n",
name, path);
return 0;
} else if (error < 0) {
mv_fdt_dprintf("Removing '%s' from '%s' node failed\n",
name, path);
return error;
} else {
mv_fdt_dprintf("Removing '%s' from '%s' node succeeded\n",
name, path);
return 0;
}
}
#endif
/*******************************************************************************
* mv_fdt_remove_node
*
* DESCRIPTION:
*
* INPUT:
* fdt.
* path.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_remove_node(void *fdt, const char *path)
{
int error;
int nodeoff;
nodeoff = mv_fdt_find_node(fdt, path);
error = fdt_del_node(fdt, nodeoff);
if (error == -FDT_ERR_NOTFOUND) {
mv_fdt_dprintf("'%s' node already removed from device tree\n",
path);
return 0;
} else if (error < 0) {
mv_fdt_dprintf("Removing '%s' node from device tree failed\n",
path);
return error;
} else {
mv_fdt_dprintf("Removing '%s' node from device tree "
"succeeded\n", path);
return 0;
}
}
/*******************************************************************************
* mv_fdt_scan_and_set_alias
*
* DESCRIPTION:
*
* INPUT:
* fdt.
* name.
* alias.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_scan_and_set_alias(void *fdt,
const char *name, const char *alias)
{
int i = 0; /* alias index */
int nodeoffset; /* current node's offset from libfdt */
int nextoffset; /* next node's offset from libfdt */
int delta; /* difference between next and current offset */
int err = 0; /* error number */
int level = 0; /* current fdt scanning depth */
char aliasname[128]; /* alias name to be stored in '/aliases' node */
char path[256] = ""; /* full path to current node */
char tmp[256]; /* auxiliary char array for extended node name*/
char *cut; /* auxiliary char pointer */
const char *node; /* node name */
uint32_t tag; /* device tree tag at given offset */
const struct fdt_property *prop;
/* Check if '/aliases' node exist. Otherwise create it */
nodeoffset = mv_fdt_find_node(fdt, "aliases");
if (nodeoffset < 0) {
mv_fdt_modify(fdt, err, fdt_add_subnode(fdt, 0, "aliases"));
if (err < 0) {
mv_fdt_dprintf("Creating '/aliases' node failed\n");
return -1;
}
nodeoffset = fdt_path_offset(fdt, "/aliases");
}
/* Check if there are pre-defined aliases and rely on them
* instead of scanning the Device Tree
*/
for (nextoffset = fdt_first_property_offset(fdt, nodeoffset);
(nextoffset >= 0);
(nextoffset = fdt_next_property_offset(fdt, nextoffset))) {
prop = fdt_get_property_by_offset(fdt, nextoffset, NULL);
if (!prop) {
nextoffset = -FDT_ERR_INTERNAL;
break;
}
if (strncmp(fdt_string(fdt, fdt32_to_cpu(prop->nameoff)),
alias, strlen(alias)) == 0) {
mv_fdt_dprintf("'%s' aliases exist. Skip scanning DT for '%s' nodes\n",
alias, name);
return 0;
}
}
/* Scan device tree for nodes that that contain 'name' substring and
* create their 'alias' with respective number */
nodeoffset = 0;
while (level >= 0) {
tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
switch (tag) {
case FDT_BEGIN_NODE:
node = fdt_get_name(fdt, nodeoffset, NULL);
sprintf(tmp, "/%s", node);
if (nodeoffset != 0)
strcat(path, tmp);
if (strstr(node, name) != NULL) {
delta = nextoffset - nodeoffset;
sprintf(aliasname, "%s%d", alias, i);
nodeoffset = mv_fdt_find_node(fdt, "aliases");
if (nodeoffset < 0)
goto alias_fail;
mv_fdt_modify(fdt, err, fdt_setprop(fdt,
nodeoffset, aliasname,
path, strlen(path)+1));
if (err < 0)
goto alias_fail;
nodeoffset = fdt_path_offset(fdt, path);
if (nodeoffset < 0)
goto alias_fail;
nextoffset = nodeoffset + delta;
mv_fdt_dprintf("Set alias %s=%s\n", aliasname,
path);
i++;
}
level++;
if (level >= MV_FDT_MAXDEPTH) {
mv_fdt_dprintf("%s: Nested too deep, "
"aborting.\n", __func__);
return -1;
}
break;
alias_fail:
mv_fdt_dprintf("Setting alias %s=%s failed\n",
aliasname, path);
return -1;
case FDT_END_NODE:
level--;
cut = strrchr(path, '/');
*cut = '\0';
if (level == 0)
level = -1; /* exit the loop */
break;
case FDT_PROP:
case FDT_NOP:
break;
case FDT_END:
mv_fdt_dprintf("Device tree scanning failed - end of "
"tree tag\n");
return -1;
default:
mv_fdt_dprintf("Device tree scanning failed - unknown "
"tag\n");
return -1;
}
nodeoffset = nextoffset;
}
return 0;
}
/*******************************************************************************
* mv_fdt_nfc_driver_type
*
* DESCRIPTION:
*
* INPUT:
* fdt.
* offset.
* check_status.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_nfc_driver_type(void *fdt, int *offset,
int check_status)
{
int nodeoffset, type;
const void *status;
const char *compat[3] = {"marvell,armada370-nand",
"marvell,armada-nand",
"marvell,armada-375-nand"};
for (type = 0; type < 3; type++) {
nodeoffset = fdt_node_offset_by_compatible(fdt, -1,
compat[type]);
if (nodeoffset < 0)
continue;
if (!check_status) {
mv_fdt_dprintf("Detected NFC driver - %s\n",
compat[type]);
break;
}
status = fdt_getprop(fdt, nodeoffset, "status", NULL);
if (!status || strncmp(status, "okay", 4) == 0) {
mv_fdt_dprintf("Enabled NFC driver - %s\n",
compat[type]);
break;
}
}
*offset = nodeoffset;
return type;
}
/*******************************************************************************
* mv_fdt_nand_mode_fixup
*
* DESCRIPTION:
* Obtain the NAND ECC value from u-boot environment and upodate FDT NAND node
* accordingly.
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_nand_mode_fixup(void *fdt)
{
u32 ecc_val;
int nfcoffset, nfc_driver_hal, err;
char *nfc_config;
char prop[128];
char propval[7];
/* Search for enabled NFC driver in DT */
nfc_driver_hal = mv_fdt_nfc_driver_type(fdt, &nfcoffset, 1);
if (nfc_driver_hal == MV_FDT_NFC_NONE) {
mv_fdt_dprintf("No NFC driver enabled in device tree\n");
return 0;
}
/* Search for 'nandEcc' parameter in the environment */
nfc_config = getenv("nandEcc");
if (!nfc_config) {
mv_fdt_dprintf("Keep default NFC configuration\n");
return 0;
}
nfc_config += strlen("nfcConfig") + 1;
/* Check for ganged mode */
if (strncmp(nfc_config, "ganged", 6) == 0) {
nfc_config += 7;
if (!nfc_driver_hal) {
mv_fdt_dprintf("NFC update: pxa3xx-nand driver "
"doesn't support ganged mode\n");
goto check_ecc;
}
sprintf(prop, "%s", "nfc,nfc-mode");
sprintf(propval, "%s", "ganged");
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nfcoffset,
prop, propval,
strlen(propval) + 1));
if (err < 0) {
mv_fdt_dprintf("NFC update: fail to modify '%s'\n",
prop);
return 0;
}
mv_fdt_dprintf("NFC update: set '%s' property to '%s'\n",
prop, propval);
}
check_ecc:
/* Check for ECC type directive */
if (strcmp(nfc_config, "1bitecc") == 0)
ecc_val = nfc_driver_hal ? MV_NFC_ECC_HAMMING : 1;
else if (strcmp(nfc_config, "4bitecc") == 0)
ecc_val = nfc_driver_hal ? MV_NFC_ECC_BCH_2K : 4;
else if (strcmp(nfc_config, "8bitecc") == 0)
ecc_val = nfc_driver_hal ? MV_NFC_ECC_BCH_1K : 8;
else if (strcmp(nfc_config, "12bitecc") == 0)
ecc_val = nfc_driver_hal ? MV_NFC_ECC_BCH_704B : 12;
else if (strcmp(nfc_config, "16bitecc") == 0)
ecc_val = nfc_driver_hal ? MV_NFC_ECC_BCH_512B : 16;
else {
mv_fdt_dprintf("NFC update: invalid nfcConfig ECC parameter - \"%s\"\n", nfc_config);
return 0;
}
if (!nfc_driver_hal)
sprintf(prop, "%s", "nand-ecc-strength");
else
sprintf(prop, "%s", "nfc,ecc-type");
mv_fdt_modify(fdt, err, fdt_setprop_u32(fdt, nfcoffset, prop,
ecc_val));
if (err < 0) {
mv_fdt_dprintf("NFC update: fail to modify'%s'\n", prop);
return -1;
}
mv_fdt_dprintf("NFC update: set '%s' property to %d\n", prop, ecc_val);
return 0;
}
static int mv_fdt_board_compatible_name_update(void *fdt)
{
char propval[256]; /* property value */
const char *prop = "compatible"; /* property name */
char node[64]; /* node name */
MV_U8 len, err;
/* set compatible property as concatenated strings.
* (i.e: compatible = "marvell,a385-db", "marvell,armada385", "marvell,armada38x";
* concatenated strings are appended after the NULL character of the previous
* sprintf wrote. This is how a DT stores multiple strings in a property.
* as a result, strlen is not relevant, and len has to be calculated */
len = mvBoardCompatibleNameGet(propval);
if (len > 0)
mv_fdt_modify(fdt, err, fdt_setprop(fdt,0 , prop, propval, len));
if (len <= 0 || err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, node);
return 0;
}
mv_fdt_dprintf("Set '%s' property to Root node\n", prop);
return 0;
}
/*******************************************************************************
* mv_fdt_update_serial
*
* DESCRIPTION:
* target : Update status field of serial nodes.
* node, properties: : -property status @ node serial
* dependencies : checks if it is a active serial port via mvUartPortGet function.
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_serial(void *fdt)
{
char propval[10]; /* property value */
const char *prop = "status"; /* property name */
char node[64]; /* node name */
int i, defaultSerialPort = mvUartPortGet();
for (i = 0; i < MV_UART_MAX_CHAN; i++) {
if (i == defaultSerialPort)
sprintf(propval, "okay"); /* Enable active Serial port node */
else
sprintf(propval, "disabled"); /* Disable non-active Serial port node */
sprintf(node, "serial@%x", MV_UART_REGS_OFFSET(i));
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
}
return 0;
}
#ifdef CONFIG_PIC_GPIO
/*******************************************************************************
* mv_fdt_update_pic_gpio
*
* DESCRIPTION: GPIO information for peripheral integrated controller (PIC)
*
* PIC GPIO pins are represented in 2 different nodes in Device Tree:
* 1. pinctrl/pic-pins-0/marvell,pins = "mpp33", "mpp34";
* 2. pm_pic/ctrl-gpios = <0x0000000c 0x00000001 0x00000001 0x0000000c 0x00000002 0x00000001>;
* Each GPIO is represented in 'ctrl-gpios' property with 3 values of 32 bits:
* '<gpios_phandle[gpio_group_num] gpio_pin_num_in_group ACTIVE_LOW>'
* i.e : for mpp 33, and given pinctrl-0 = <0x0000000b> :
* gpio_group_num = 33/32 = 1
* gpio_pin_num_in_group= 33%32 = 1
* ACTIVE_LOW = 1
* DT entry is : '<0x0000000c 0x00000001 0x00000001>';
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
#define MAX_GPIO_NUM 10
static int mv_fdt_update_pic_gpio(void *fdt)
{
const char *picPinsNode = "pic-pins-0";
const char *marvell_pins_prop = "marvell,pins";
const char *pm_picNode = "pm_pic";
const char *ctrl_gpios_prop = "ctrl-gpios";
MV_U32 picGpioInfo[MAX_GPIO_NUM];
MV_U32 ctrl_gpios_prop_value[3*MAX_GPIO_NUM]; /* 3*32bit is required per GPIO */
char propval[256] = "";
int err, len = 0, nodeoffset, i, gpioMaxNum = mvBoardPICGpioGet(picGpioInfo);
MV_U32 gpios_phandle[2]; /* phandle values of gpio nodes */
MV_U32 gpio_offset;
char gpio_node[30] = "";
/* if board has no dedicated PIC MPP Pins: remove 'pm_pic' & 'pinctrl/pic-pins-0' */
if (gpioMaxNum <= 0) {
mv_fdt_dprintf("'pic-pins-0' & 'pm_pic' nodes removed: no dedicated PIC GPIO Pins\n");
if (mv_fdt_remove_node(fdt, picPinsNode))
mv_fdt_dprintf("Failed to remove %s\n", picPinsNode);
if (mv_fdt_remove_node(fdt, pm_picNode))
mv_fdt_dprintf("Failed to remove %s\n", pm_picNode);
return 0;
}
/* fetch gpio nodes' phandle.
* every GPIO group have GPIO node id DT
* and for each GPIO pin, an entry is added to the ctrl-gpios,
* in order to link between the entry and the relevant gpio node for the relevant
* group, the phandle of the gpio node is added to the entry */
for (i = 0; i < 2; ++i) {
sprintf(gpio_node, "gpio@%X", MV_GPP_REGS_OFFSET(i));
gpio_offset = mv_fdt_find_node(fdt, gpio_node);
gpios_phandle[i] = fdt_get_phandle(fdt, gpio_offset);
if (!gpios_phandle[i]) {
mvOsPrintf("Failed updating suspend-resume GPIO pin settings in DT\n");
return 0;
}
}
/* set pinctrl/pic-pins-0/marvell,pins property as concatenated strings.
* (i.e : marvell,pins = "mpp33", "mpp34", "mpp35")
* append next string after the NULL character that the previous
* sprintf wrote. This is how a DT stores multiple strings in a property.*/
for (i = 0 ; i < gpioMaxNum ; i++)
len += sprintf(propval + len, "mpp%d", picGpioInfo[i]) + 1;
nodeoffset = mv_fdt_find_node(fdt, picPinsNode); /* find 'pic-pins-0' node */
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, marvell_pins_prop, propval, len));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", marvell_pins_prop, picPinsNode);
return 0;
}
mv_fdt_dprintf("Set '%s' property in '%s' node\n", marvell_pins_prop, picPinsNode);
/* set pm_pic/ctrl-gpios property: */
nodeoffset = mv_fdt_find_node(fdt, pm_picNode); /* find 'pm_pic' node */
if (len == 0) {
printf("Empty property value\n");
return 0;
}
/* Each GPIO is represented in 'ctrl-gpios' property with 3 values of 32 bits:
* '<gpios_phandle[gpio_group_num] gpio_pin_num_in_group ACTIVE_LOW>'
* i.e : for mpp 33, and given pinctrl-0 = <0x0000000b> :
* gpio_group_num = 33/32 = 1
* gpio_pin_num_in_group= 33%32 = 1
* ACTIVE_LOW = 1
* DT entry is : '<0x0000000c 0x00000001 0x00000001>'; */
for (i = 0 ; i < gpioMaxNum ; i++) {
/* pinctrl_0_base +gpio Group num */
ctrl_gpios_prop_value[3*i] = htonl(gpios_phandle[picGpioInfo[i] / 32]);
/* 32 pins in each group */
ctrl_gpios_prop_value[3*i + 1] = htonl(picGpioInfo[i] % 32);
/* GPIO_ACTIVE_LOW = 1 */
ctrl_gpios_prop_value[3*i + 2] = htonl(0x1);
}
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, ctrl_gpios_prop, ctrl_gpios_prop_value, 4*gpioMaxNum*3));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", ctrl_gpios_prop, pm_picNode);
return 0;
}
mv_fdt_dprintf("Set '%s' property in '%s' node\n", ctrl_gpios_prop, pm_picNode);
return 0;
}
#endif /* CONFIG_PIC_GPIO */
#ifdef MV_INCLUDE_SWITCH
/*******************************************************************************
* mv_fdt_update_switch
*
* DESCRIPTION:
* This routines updates switch node and ethernet-phy node on status and rgmii_(t)rx_timing_delay properties
* according to Switch existence and Device ID detection, this route also set ethernet-phy@i node on reg property
* if ethi is connected to switch
*
* INPUT:
* fdt.
*
* OUTPUT:
* None.
*
* RETURN:
* -1 on error os 0 otherwise.
*
*******************************************************************************/
static int mv_fdt_update_switch(void *fdt)
{
char propval[10]; /* property value */
u32 propval_u32 = 0;
char prop[50]; /* property name */
char node[64]; /* node name */
MV_U16 switch_device_id;
int nodeoffset; /* node offset from libfdt */
int err;
switch_device_id = mvEthSwitchGetDeviceID();
/*if switch module is connected and detect correct switch device ID, enable switch in fdt*/
if (switch_device_id > 0)
sprintf(propval, "okay");
else
sprintf(propval, "disabled");
sprintf(node, "switch");
sprintf(prop, "status");
if (mv_fdt_set_node_prop(fdt, node, prop, propval) < 0) {
mv_fdt_dprintf("Failed to set property '%s' of node '%s' in device tree\n", prop, node);
return 0;
}
if (mvBoardIsSwitchConnected()) {
int i = 0;
switch_device_id >>= QD_REG_SWITCH_ID_PRODUCT_NUM_OFFSET;
for (i = 0; i < mvCtrlEthMaxPortGet(); i++) {
/*change the switch connected eth ports' phy to 999*/
/*999 mean that ethernet is not connected to phy*/
if (mvBoardSwitchConnectedPortGet(i) != -1) {
sprintf(node, "ethernet-phy@%d", i);
sprintf(prop, "reg");
propval_u32 = 999;
nodeoffset = mv_fdt_find_node(fdt, node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", node);
return -1;
}
/* Setup PHY address in DT in "reg" property of an appropriate PHY node.
This value is HEX number, not a string, and uses network byte order */
propval_u32 = htonl(propval_u32);
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop,
&propval_u32, sizeof(propval_u32)));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, node);
return -1;
}
}
}
/*for E6172 switch, rgmii_rx_timing_delay and rgmii_tx_timing_delay should be enabled*/
if (switch_device_id == MV_E6172_PRODUCT_NUM) {
/*set rgmii_rx_timing_delay and rgmii_tx_timing_delay to be enabled*/
sprintf(node, "switch");
sprintf(prop, "rgmii_rx_timing_delay");
/*enable delay to RXCLK for IND inputs when port is in RGMII mode*/
propval_u32 = 1;
nodeoffset = mv_fdt_find_node(fdt, node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", node);
return -1;
}
/* Setup rgmii_rx_timing_delay in DT.
This value is HEX number, not a string, and uses network byte order */
propval_u32 = htonl(propval_u32);
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop, &propval_u32, sizeof(propval_u32)));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, node);
return -1;
}
sprintf(node, "switch");
sprintf(prop, "rgmii_tx_timing_delay");
/*enable delay to GTXCLK for OUTD outputs when port is in RGMII mode*/
propval_u32 = 1;
nodeoffset = mv_fdt_find_node(fdt, node);
if (nodeoffset < 0) {
mv_fdt_dprintf("Lack of '%s' node in device tree\n", node);
return -1;
}
/* Setup rgmii_tx_timing_delay in DT.
This value is HEX number, not a string, and uses network byte order */
propval_u32 = htonl(propval_u32);
mv_fdt_modify(fdt, err, fdt_setprop(fdt, nodeoffset, prop, &propval_u32, sizeof(propval_u32)));
if (err < 0) {
mv_fdt_dprintf("Modifying '%s' in '%s' node failed\n", prop, node);
return -1;
}
}
}
return 0;
}
#endif
#endif