922 lines
22 KiB
C
Executable File
922 lines
22 KiB
C
Executable File
/*
|
|
* Copyright (C) 2015 Stefan Roese <sr@denx.de>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
#undef DEBUG
|
|
#include <common.h>
|
|
#include <i2c.h>
|
|
#include <miiphy.h>
|
|
#include <netdev.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/cpu.h>
|
|
#include <asm/arch/soc.h>
|
|
#include <mmc.h>
|
|
#include <spl.h>
|
|
#include <linux/mbus.h>
|
|
#include <environment.h>
|
|
#include <fdt_support.h>
|
|
#include <dm.h>
|
|
#include <wdt.h>
|
|
|
|
#include <asm/gpio.h>
|
|
|
|
#include "nbhw_gpio.h"
|
|
|
|
#include "../common/nbhw_init.h"
|
|
#include "../common/nbhw_env.h"
|
|
#include "../common/nbhw_bd.h"
|
|
|
|
#include "../drivers/ddr/marvell/a38x/ddr3_a38x_topology.h"
|
|
#include <../serdes/a38x/high_speed_env_spec.h>
|
|
|
|
#include "da9063.h"
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
/*
|
|
* Those values and defines are taken from the Marvell U-Boot version
|
|
* "u-boot-2013.01-2014_T3.0"
|
|
*/
|
|
#define GPP_OUT_ENA_LOW (~(BIT(6) | BIT(19) | BIT(29))) /* 1=Input, default input */
|
|
|
|
#define GPP_OUT_ENA_MID (~(BIT(12) | BIT(15)))
|
|
|
|
#define GPP_OUT_VAL_LOW (BIT(29))
|
|
#define GPP_OUT_VAL_MID (BIT(15))
|
|
#define GPP_POL_LOW 0x0
|
|
#define GPP_POL_MID 0x0
|
|
|
|
#define BD_EEPROM_ADDR (0x50) /* CPU BD EEPROM (8kByte) is at 50 (A0) */
|
|
#define BD_ADDRESS (0x0000) /* Board descriptor at beginning of EEPROM */
|
|
#define PD_ADDRESS (0x0200) /* Product descriptor */
|
|
#define PARTITION_ADDRESS (0x0600) /* Partition Table */
|
|
#define SERDES_CONFIG_ADDRESS (0x0800) /* SERDES config address */
|
|
|
|
#define DEV_CS0_BASE 0xfd000000
|
|
|
|
static struct serdes_map board_serdes_map[] = {
|
|
{ SGMII0, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0 },
|
|
{ USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0 },
|
|
{ SGMII1, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0 },
|
|
{ PEX3, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0 },
|
|
{ PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0 },
|
|
{ SGMII2, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0 }
|
|
};
|
|
|
|
enum serdes_type get_serdes_type(int index)
|
|
{
|
|
if ((index>=0) && (index <ARRAY_SIZE(board_serdes_map)))
|
|
{
|
|
return board_serdes_map[index].serdes_type;
|
|
} else {
|
|
return LAST_SERDES_TYPE;
|
|
}
|
|
}
|
|
|
|
static BD_Context bdctx[3]; /* The descriptor context */
|
|
|
|
static int _bd_init(void)
|
|
{
|
|
if (bd_get_context(&bdctx[0], BD_EEPROM_ADDR, BD_ADDRESS) != 0) {
|
|
printf("%s() no valid bd found\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
if (bd_get_context(&bdctx[1], BD_EEPROM_ADDR, PD_ADDRESS) != 0) {
|
|
/* Ignore that, legacy boxes don't have a pd */
|
|
}
|
|
|
|
if (bd_get_context(&bdctx[2], BD_EEPROM_ADDR, PARTITION_ADDRESS) != 0) {
|
|
printf("%s() no valid partition table found\n", __func__);
|
|
}
|
|
|
|
bd_register_context_list(bdctx, ARRAY_SIZE(bdctx));
|
|
|
|
return 0;
|
|
}
|
|
|
|
typedef struct EEPROM_SERDES_CONFIG {
|
|
uint8_t magic[2];
|
|
uint8_t version;
|
|
uint8_t spare_0;
|
|
uint8_t serdes_cfg[6];
|
|
uint8_t spare1[2];
|
|
uint32_t crc32;
|
|
} EEPROM_SERDES_CONFIG;
|
|
|
|
static struct EEPROM_SERDES_CONFIG eeprom_serdes_config;
|
|
|
|
static void read_eeprom_serdes_config(void)
|
|
{
|
|
uint32_t crc;
|
|
if (i2c_read(BD_EEPROM_ADDR, SERDES_CONFIG_ADDRESS, 2, (uint8_t *)&eeprom_serdes_config, sizeof(eeprom_serdes_config))) {
|
|
goto fail;
|
|
}
|
|
|
|
crc = crc32(0, (uint8_t *)&eeprom_serdes_config, sizeof(eeprom_serdes_config)-4);
|
|
|
|
if ((eeprom_serdes_config.magic[0] != 0x83) ||
|
|
(eeprom_serdes_config.magic[1] != 0xfb) ||
|
|
(eeprom_serdes_config.version != 0x01) ||
|
|
(eeprom_serdes_config.crc32 != crc))
|
|
{
|
|
goto fail;
|
|
}
|
|
|
|
printf("Valid user serdes config found\n");
|
|
return;
|
|
|
|
fail:
|
|
memset(&eeprom_serdes_config, 0xff, sizeof(eeprom_serdes_config));
|
|
printf("No user serdes config found\n");
|
|
return;
|
|
}
|
|
|
|
static uint8_t get_eeprom_serdes_config(int serdes_index)
|
|
{
|
|
if ((serdes_index<0) || (serdes_index>=sizeof(eeprom_serdes_config.serdes_cfg))) return 0xff;
|
|
return eeprom_serdes_config.serdes_cfg[serdes_index];
|
|
}
|
|
|
|
|
|
static inline int __maybe_unused read_eeprom(void)
|
|
{
|
|
int res = _bd_init();
|
|
|
|
read_eeprom_serdes_config();
|
|
|
|
return res;
|
|
}
|
|
|
|
/* TODO: Create DA9063 Accessor Module */
|
|
#define CONFIG_PMIC_I2C_BUS 0
|
|
#define CONFIG_PMIC_I2C_ADDR 0x58 /* Pages 0 and 1, Pages 2 and 3 -> 0x59 */
|
|
|
|
#define PMIC_REG_GPIO_MODE0_7 0x1D /* Control register for GPIOs 0..7 */
|
|
#define PMIC_REG_GPIO_MODE8_15 0x1E /* Control register for GPIOs 8..15 */
|
|
|
|
#define PMIC_REG_BBAT_CONT 0xC5 /* Control register for backup battery */
|
|
|
|
|
|
static int da9063_i2c_bus = 0;
|
|
|
|
void da9063_init(int i2c_bus)
|
|
{
|
|
da9063_i2c_bus = i2c_bus;
|
|
}
|
|
|
|
int da9093_get_reg(int reg, u8* val)
|
|
{
|
|
int ret;
|
|
int old_bus;
|
|
u8 temp;
|
|
|
|
/* TODO: Check whether switching is required */
|
|
old_bus = i2c_get_bus_num();
|
|
i2c_set_bus_num(da9063_i2c_bus);
|
|
|
|
/* TODO: Use CONFIG_PMIC_I2C_ADDR+1 if reg > 0xFF */
|
|
*val = 0;
|
|
ret = i2c_read(CONFIG_PMIC_I2C_ADDR, reg, 1, &temp, 1);
|
|
if (ret == 0)
|
|
*val = temp;
|
|
|
|
i2c_set_bus_num(old_bus);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int da9093_set_reg(int reg, u8 val)
|
|
{
|
|
int ret;
|
|
int old_bus;
|
|
|
|
/* TODO: Check whether switching is required */
|
|
old_bus = i2c_get_bus_num();
|
|
i2c_set_bus_num(da9063_i2c_bus);
|
|
|
|
/* TODO: Use CONFIG_PMIC_I2C_ADDR+1 if reg > 0xFF */
|
|
ret = i2c_write(CONFIG_PMIC_I2C_ADDR, reg, 1, &val, 1);
|
|
if (ret != 0)
|
|
puts("da9063 write error\n");
|
|
|
|
i2c_set_bus_num(old_bus);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void da9063_set_gpio(unsigned bit, int state)
|
|
{
|
|
int pmic_reg;
|
|
int ret;
|
|
u8 bitmask;
|
|
u8 reg = 0x00;
|
|
|
|
if (bit <= 7) {
|
|
pmic_reg = PMIC_REG_GPIO_MODE0_7;
|
|
bitmask = 1U << (bit-0);
|
|
}
|
|
else {
|
|
pmic_reg = PMIC_REG_GPIO_MODE8_15;
|
|
bitmask = 1U << (bit-8);
|
|
}
|
|
|
|
/* printf("da9063_set_gpio %d 0x%04x\n", pmic_reg, bitmask); */
|
|
ret = da9093_get_reg(pmic_reg, ®);
|
|
|
|
if (ret == 0) {
|
|
if (state) reg |= bitmask;
|
|
else reg &= ~bitmask;
|
|
|
|
(void)da9093_set_reg(pmic_reg, reg);
|
|
}
|
|
}
|
|
|
|
extern int console_init_f(void);
|
|
|
|
static int init_console(void)
|
|
{
|
|
int ret;
|
|
struct udevice *dev;
|
|
char *consoledev;
|
|
char buf[16] = "serial@12100";
|
|
|
|
debug("init console\n");
|
|
|
|
/* Make sure all devices are probed, it seems
|
|
* that this stuff is buggy in U-Boot */
|
|
ret = uclass_first_device(UCLASS_SERIAL, &dev);
|
|
if (ret) {
|
|
printf("Could not find any serial device\n");
|
|
return ret;
|
|
}
|
|
|
|
while (list_is_last(&dev->uclass_node, &dev->uclass->dev_head) == 0) {
|
|
uclass_next_device(&dev);
|
|
}
|
|
|
|
set_console();
|
|
|
|
/* Don't use external console, if we have no FPGA,
|
|
as it cannot work then. -> Switch to internal console then. */
|
|
if (readw(0xfd000000)!=0x012f) { /* Check for correct FPGA signature */
|
|
printf("FPGA not ready. Forcing console to ttyS0\n");
|
|
env_set("defaultconsole", "ttyS0");
|
|
env_set("consoledev", "ttyS0");
|
|
}
|
|
|
|
consoledev = env_get("consoledev");
|
|
if (strncmp(consoledev, "ttyS0", 5) == 0) {
|
|
strncpy(buf, "serial@12000", sizeof(buf));
|
|
}
|
|
|
|
env_set("stdin", buf);
|
|
env_set("stdout", buf);
|
|
env_set("stderr", buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hws_board_topology_load(struct serdes_map **serdes_map_array, u8 *count)
|
|
{
|
|
int i;
|
|
|
|
if (read_eeprom() < 0){
|
|
puts("Could not read board descriptor using default serdes config.\n");
|
|
|
|
board_serdes_map[0].serdes_speed = SERDES_SPEED_1_25_GBPS;
|
|
board_serdes_map[0].serdes_mode = SERDES_DEFAULT_MODE;
|
|
board_serdes_map[0].serdes_type = SGMII0;
|
|
|
|
board_serdes_map[1].serdes_speed = SERDES_SPEED_5_GBPS;
|
|
board_serdes_map[1].serdes_mode = PEX_ROOT_COMPLEX_X1;
|
|
board_serdes_map[1].serdes_type = USB3_HOST0;
|
|
|
|
board_serdes_map[2].serdes_speed = SERDES_SPEED_1_25_GBPS;
|
|
board_serdes_map[2].serdes_mode = SERDES_DEFAULT_MODE;
|
|
board_serdes_map[2].serdes_type = SGMII1;
|
|
|
|
board_serdes_map[3].serdes_speed = SERDES_SPEED_5_GBPS;
|
|
board_serdes_map[3].serdes_mode = PEX_ROOT_COMPLEX_X1;
|
|
board_serdes_map[3].serdes_type = PEX3;
|
|
|
|
board_serdes_map[4].serdes_speed = SERDES_SPEED_5_GBPS;
|
|
board_serdes_map[4].serdes_mode = PEX_ROOT_COMPLEX_X1;
|
|
board_serdes_map[4].serdes_type = PEX2;
|
|
|
|
board_serdes_map[5].serdes_speed = SERDES_SPEED_1_25_GBPS;
|
|
board_serdes_map[5].serdes_mode = SERDES_DEFAULT_MODE;
|
|
board_serdes_map[5].serdes_type = SGMII2;
|
|
} else {
|
|
for (i = 0; i < ARRAY_SIZE(board_serdes_map); i++) {
|
|
enum serdes_type type;
|
|
uint8_t user_config = get_eeprom_serdes_config(i);
|
|
|
|
if (user_config != 0xff) {
|
|
/* if we have a user config we use that one */
|
|
type = (enum serdes_type)user_config;
|
|
} else {
|
|
/* otherwise we use the config from the bd */
|
|
type = bd_get_serdes_type(i);
|
|
}
|
|
|
|
/* Do not touch serdes */
|
|
if (type < LAST_SERDES_TYPE) {
|
|
if ((type >= SGMII0) && (type <= SGMII2)) {
|
|
board_serdes_map[i].serdes_speed = SERDES_SPEED_1_25_GBPS;
|
|
board_serdes_map[i].serdes_mode = SERDES_DEFAULT_MODE;
|
|
}
|
|
else if ((type >= PEX0) && (type <= PEX3)) {
|
|
board_serdes_map[i].serdes_speed = SERDES_SPEED_5_GBPS;
|
|
board_serdes_map[i].serdes_mode = PEX_ROOT_COMPLEX_X1;
|
|
}
|
|
else if ((type >= USB3_HOST0) && (type <= USB3_HOST1)) {
|
|
board_serdes_map[i].serdes_speed = SERDES_SPEED_5_GBPS;
|
|
board_serdes_map[i].serdes_mode = PEX_ROOT_COMPLEX_X1;
|
|
} else if ((type == DEFAULT_SERDES)) {
|
|
board_serdes_map[i].serdes_speed = SERDES_SPEED_1_25_GBPS;
|
|
board_serdes_map[i].serdes_mode = SERDES_DEFAULT_MODE;
|
|
} else {
|
|
printf("SERDES Type %d not supported\n", type);
|
|
/* Keep default serdes configuration */
|
|
type = board_serdes_map[i].serdes_type;
|
|
}
|
|
|
|
if (i==3) {
|
|
/* On V2 TX line for PCIe slot1 is inverted*/
|
|
board_serdes_map[i].swap_tx = 1;
|
|
}
|
|
|
|
debug("Configure SERDES %d to %d\n", i, type);
|
|
|
|
board_serdes_map[i].serdes_type = type;
|
|
}
|
|
}
|
|
}
|
|
|
|
*serdes_map_array = board_serdes_map;
|
|
*count = ARRAY_SIZE(board_serdes_map);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Define the DDR layout / topology here in the board file. This will
|
|
* be used by the DDR3 init code in the SPL U-Boot version to configure
|
|
* the DDR3 controller.
|
|
*/
|
|
static struct hws_topology_map board_topology_map = {
|
|
0x1, /* active interfaces */
|
|
/* cs_mask, mirror, dqs_swap, ck_swap X PUPs */
|
|
{ { { {0x1, 0, 0, 0},
|
|
{0x1, 0, 0, 0},
|
|
{0x1, 0, 0, 0},
|
|
{0x1, 0, 0, 0},
|
|
{0x1, 0, 0, 0} },
|
|
SPEED_BIN_DDR_1600K, /* speed_bin */
|
|
BUS_WIDTH_16, /* memory_width */
|
|
MEM_4G, /* mem_size */
|
|
DDR_FREQ_667, /* frequency */
|
|
7, 9, /* cas_l cas_wl */
|
|
HWS_TEMP_HIGH} }, /* temperature */
|
|
5, /* Num Of Bus Per Interface*/
|
|
BUS_MASK_32BIT /* Busses mask */
|
|
};
|
|
|
|
struct hws_topology_map *ddr3_get_topology_map(void)
|
|
{
|
|
/* Return the board topology as defined in the board code */
|
|
return &board_topology_map;
|
|
}
|
|
|
|
#if defined(CONFIG_WATCHDOG)
|
|
|
|
void watchdog_init(void)
|
|
{
|
|
/* NOTE: Global watchdog counter register is at 0xf1020334
|
|
Could not find this in the manual. */
|
|
if (uclass_get_device(UCLASS_WDT, 0, (struct udevice **)&(gd->watchdog))) {
|
|
puts("Cannot enable watchdog!\n");
|
|
} else {
|
|
puts("Enabling watchdog\n");
|
|
wdt_start(gd->watchdog, (u32) 25000000 * 150, 0); /* Timer runs at 25 MHz */
|
|
}
|
|
}
|
|
|
|
/* Called by macro WATCHDOG_RESET */
|
|
void watchdog_reset(void)
|
|
{
|
|
static ulong next_reset = 0;
|
|
ulong now;
|
|
|
|
if (!(gd->watchdog)) return;
|
|
|
|
now = timer_get_us();
|
|
|
|
/* Do not reset the watchdog too often */
|
|
if (now > next_reset) {
|
|
wdt_reset(gd->watchdog);
|
|
next_reset = now + 1000000;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int board_early_init_f(void)
|
|
{
|
|
/* Configure MPP */
|
|
writel(0x00111111, MVEBU_MPP_BASE + 0x00);
|
|
writel(0x40000000, MVEBU_MPP_BASE + 0x04);
|
|
writel(0x55000444, MVEBU_MPP_BASE + 0x08);
|
|
writel(0x55053350, MVEBU_MPP_BASE + 0x0c);
|
|
writel(0x55555555, MVEBU_MPP_BASE + 0x10);
|
|
writel(0x06605505, MVEBU_MPP_BASE + 0x14);
|
|
writel(0x55550555, MVEBU_MPP_BASE + 0x18);
|
|
writel(0x00005551, MVEBU_MPP_BASE + 0x1c);
|
|
|
|
/* Set GPP Out value */
|
|
writel(GPP_OUT_VAL_LOW, MVEBU_GPIO0_BASE + 0x00);
|
|
writel(GPP_OUT_VAL_MID, MVEBU_GPIO1_BASE + 0x00);
|
|
|
|
/* Set GPP Polarity */
|
|
writel(GPP_POL_LOW, MVEBU_GPIO0_BASE + 0x0c);
|
|
writel(GPP_POL_MID, MVEBU_GPIO1_BASE + 0x0c);
|
|
|
|
/* Set GPP Out Enable */
|
|
writel(GPP_OUT_ENA_LOW, MVEBU_GPIO0_BASE + 0x04);
|
|
writel(GPP_OUT_ENA_MID, MVEBU_GPIO1_BASE + 0x04);
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 spl_boot_mode(const u32 boot_device)
|
|
{
|
|
return MMCSD_MODE_EMMCBOOT;
|
|
}
|
|
|
|
void spl_board_init(void)
|
|
{
|
|
int err;
|
|
struct mmc *mmcp;
|
|
|
|
err = mmc_initialize(0);
|
|
if (err)
|
|
return;
|
|
|
|
debug("SPL: select partition\n");
|
|
mmcp = find_mmc_device(0);
|
|
mmc_init(mmcp);
|
|
printf("Partition found: %p\n", mmcp);
|
|
/* select boot0 as first boot partition */
|
|
mmcp->part_config &= ~(PART_ACCESS_MASK << 3);
|
|
mmcp->part_config |= (1 << 3);
|
|
debug("Boot partition set to boot0\n");
|
|
}
|
|
|
|
static void set_gpios(void)
|
|
{
|
|
init_gpios();
|
|
}
|
|
|
|
#if !defined(CONFIG_SPL_BUILD)
|
|
|
|
static void pass_hw_rev(void)
|
|
{
|
|
int hw_ver = 0, hw_rev = 0;
|
|
char *old_env;
|
|
char hw_versions[128];
|
|
char new_env[256];
|
|
|
|
bd_get_hw_version(&hw_ver, &hw_rev);
|
|
|
|
snprintf(hw_versions, sizeof(hw_versions), "CP=%d.%d",
|
|
hw_ver, hw_rev);
|
|
|
|
old_env = env_get("add_version_bootargs");
|
|
|
|
/* Normaly add_version_bootargs should already be set (see board include file) */
|
|
if (old_env != 0) {
|
|
snprintf(new_env, sizeof(new_env), "%s %s", old_env, hw_versions);
|
|
}
|
|
else {
|
|
snprintf(new_env, sizeof(new_env), "env_set bootargs $bootargs %s\n", hw_versions);
|
|
}
|
|
|
|
env_set("add_version_bootargs", new_env);
|
|
}
|
|
|
|
#endif
|
|
|
|
int board_init(void)
|
|
{
|
|
#if defined(CONFIG_WATCHDOG)
|
|
watchdog_init();
|
|
#endif
|
|
|
|
/* adress of boot parameters */
|
|
gd->bd->bi_boot_params = mvebu_sdram_bar(0) + 0x100;
|
|
|
|
/* Setup the MBUS mapping for Devicebus CS0 */
|
|
mbus_dt_setup_win(&mbus_state, DEV_CS0_BASE, 16 << 20,
|
|
CPU_TARGET_DEVICEBUS_BOOTROM_SPI, CPU_ATTR_DEV_CS0);
|
|
|
|
if (read_eeprom() < 0)
|
|
puts("Could not get board ID.\n");
|
|
|
|
set_gpios();
|
|
|
|
/* @@se: With this call we disable a debug feature, that allows one to read out the memory
|
|
* over i2c at slave address 0x64. This feature is for some undocumented reasons enabled by default
|
|
* the default value is 0x00370010, according to the documentation it should be 0x00330010. If we set
|
|
* it back to this value address 0x64 is unused again. This is necessary because atsha204 uses 0x64 as
|
|
* slave address.
|
|
*/
|
|
writel(0x00330010, INTER_REGS_BASE + 0x1108C);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int board_early_init_r(void)
|
|
{
|
|
/* We need to force mmc_initialization here, so that
|
|
* we have mmc available for read of /boot/consoledev */
|
|
mmc_initialize(gd->bd);
|
|
/* We need to force the env relocation so that it won't overwritte the
|
|
* serial devices that we set in init_console */
|
|
env_relocate();
|
|
gd->env_valid = ENV_VALID;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(CONFIG_SPL_BUILD)
|
|
|
|
static bool get_button_state(void)
|
|
{
|
|
u8 state = 0x00;
|
|
|
|
(void)da9093_get_reg(PMIC_REG_STATUS_A, &state);
|
|
|
|
return (state & 0x01) == 0x01;
|
|
}
|
|
|
|
static int check_reset_button(void)
|
|
{
|
|
int counter = 0;
|
|
|
|
/* Check how long button is pressed */
|
|
do {
|
|
if (!get_button_state())
|
|
break;
|
|
|
|
udelay(100*1000); /* 100ms */
|
|
counter += 100;
|
|
|
|
if (counter == 2000) {
|
|
/* Indicate factory reset threshold */
|
|
|
|
/* let all green LEDs blink up */
|
|
set_led(LED0_GREEN, 1);
|
|
set_led(LED1_GREEN, 1);
|
|
set_led(LED2_GREEN, 1);
|
|
set_led(LED3_GREEN, 1);
|
|
set_led(LED4_GREEN, 1);
|
|
set_led(LED5_GREEN, 1);
|
|
udelay(400000); /* 400ms */
|
|
set_led(LED1_GREEN, 0);
|
|
set_led(LED2_GREEN, 0);
|
|
set_led(LED3_GREEN, 0);
|
|
set_led(LED4_GREEN, 0);
|
|
set_led(LED5_GREEN, 0);
|
|
} else if (counter == 12000) {
|
|
/* Indicate recovery boot threshold */
|
|
|
|
/* let all red LEDs blink up */
|
|
set_led(LED0_GREEN, 0);
|
|
set_led(LED0_RED, 1);
|
|
set_led(LED1_RED, 1);
|
|
set_led(LED2_RED, 1);
|
|
set_led(LED3_RED, 1);
|
|
set_led(LED4_RED, 1);
|
|
set_led(LED5_RED, 1);
|
|
udelay(400000); /* 400ms */
|
|
set_led(LED0_RED, 0);
|
|
set_led(LED1_RED, 0);
|
|
set_led(LED2_RED, 0);
|
|
set_led(LED3_RED, 0);
|
|
set_led(LED4_RED, 0);
|
|
set_led(LED5_RED, 0);
|
|
set_led(LED0_GREEN, 1);
|
|
}
|
|
} while (counter < 12000);
|
|
|
|
if (counter < 2000) {
|
|
/* Don't do anything for duration < 2s */
|
|
}
|
|
else if (counter < 12000)
|
|
{
|
|
/* Do factory reset for duration between 2s and 12s */
|
|
char new_bootargs[512];
|
|
char *bootargs = env_get("bootargs");
|
|
|
|
if (bootargs == 0) bootargs="";
|
|
|
|
printf("Do factory reset during boot...\n");
|
|
|
|
strncpy(new_bootargs, bootargs, sizeof(new_bootargs));
|
|
strncat(new_bootargs, " factory-reset", sizeof(new_bootargs));
|
|
|
|
env_set("bootargs", new_bootargs);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
/* Boot into recovery for duration > 12s */
|
|
|
|
printf("Booting recovery image...\n");
|
|
|
|
/* Set bootcmd to run recovery */
|
|
env_set("bootcmd", "run recovery");
|
|
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int misc_init_r(void)
|
|
{
|
|
/* Because U-Boot is buggy, we need to call this funktion again
|
|
* it will print the pre console buffer */
|
|
|
|
/* TODO: Moved following two lines to board_late_init because ttyS1 is currently not working without loaded bitstream */
|
|
// init_console();
|
|
// console_init_f();
|
|
|
|
/* Configure PMIC */
|
|
|
|
#if !defined(CONFIG_SPL_BUILD)
|
|
|
|
/* Enable PMIC LED */
|
|
da9093_set_reg(0x1e, 0x08);
|
|
|
|
/* Enable PMIC RTC backup charger (charge with 6mA to 3.1V) */
|
|
da9093_set_reg(0xc5, 0xff);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void set_phy_page(const char *miidev, int phy_addr, int page)
|
|
{
|
|
miiphy_write(miidev, phy_addr, 22, page);
|
|
}
|
|
|
|
static void set_phy_fast_blink_mode(int phy_addr)
|
|
{
|
|
const char *miidev = miiphy_get_current_dev();
|
|
debug ("miidev: %s\n", miidev);
|
|
|
|
set_phy_page(miidev, phy_addr, 3);
|
|
miiphy_write(miidev, phy_addr, 16, 0x1032);
|
|
miiphy_write(miidev, phy_addr, 17, 0x4405);
|
|
miiphy_write(miidev, phy_addr, 18, 0x4A08);
|
|
set_phy_page(miidev, phy_addr, 0);
|
|
}
|
|
|
|
#if !defined(CONFIG_SPL_BUILD)
|
|
|
|
static void set_devicetree_name(void)
|
|
{
|
|
char devicetreename[64];
|
|
/* add hardware versions to environment */
|
|
if (bd_get_devicetree(devicetreename, sizeof(devicetreename)) != 0) {
|
|
printf("Devicetree name not found, use legacy name\n");
|
|
strcpy(devicetreename, "armada-385-nbhw18-prod1.dtb");
|
|
}
|
|
|
|
env_set("fdt_image", devicetreename);
|
|
}
|
|
|
|
#endif
|
|
|
|
int board_late_init(void)
|
|
{
|
|
#if !defined(CONFIG_SPL_BUILD)
|
|
gpio_request(29, "RST_ETH_PHY_N");
|
|
gpio_direction_output(29, 0);
|
|
|
|
find_and_set_active_partition();
|
|
pass_hw_rev();
|
|
|
|
/* Todo: It seems that something with the network is wrong */
|
|
run_command("run load_fpga", CMD_FLAG_ENV);
|
|
|
|
#endif
|
|
|
|
/* TODO: Move the following two lines up to misc_init_r when ttyS1 works without FPGA again */
|
|
init_console();
|
|
console_init_f();
|
|
|
|
#if !defined(CONFIG_SPL_BUILD)
|
|
|
|
check_reset_button();
|
|
|
|
set_devicetree_name();
|
|
|
|
set_mac_addresses(3);
|
|
|
|
/* Take phy out of reset after FPGA was loaded */
|
|
gpio_set_value(29, 1);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
void configure_mvswitch(void);
|
|
|
|
int board_network_enable(struct mii_dev *bus)
|
|
{
|
|
static int NETWORK_ENABLED = 0;
|
|
|
|
if (!NETWORK_ENABLED) {
|
|
set_phy_fast_blink_mode(0);
|
|
set_phy_fast_blink_mode(1);
|
|
configure_mvswitch();
|
|
|
|
NETWORK_ENABLED = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int checkboard(void)
|
|
{
|
|
debug("Board: NetModule NBHW18\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int board_fit_config_name_match(const char *name)
|
|
{
|
|
#ifdef CONFIG_SPL_BUILD
|
|
/* SPL has enabled serial0 per default and will output everything
|
|
* independend of /boot/consoledev */
|
|
#define DEFAULT_DTB_NAME "armada-385-nbhw18-spl"
|
|
#else
|
|
/* U-Boot will read /boot/consoledev and based on that it
|
|
* enables its own serial console */
|
|
#define DEFAULT_DTB_NAME "armada-385-nbhw18-v2"
|
|
#endif
|
|
|
|
/* Check if name fits our dts */
|
|
return strcmp(DEFAULT_DTB_NAME, name);
|
|
}
|
|
|
|
#ifdef CONFIG_OF_BOARD_FIXUP
|
|
|
|
|
|
int fdt_enable_by_ofname(void *rw_fdt_blob, char *ofname)
|
|
{
|
|
int offset = fdt_path_offset(rw_fdt_blob, ofname);
|
|
|
|
return fdt_status_okay(rw_fdt_blob, offset);
|
|
}
|
|
|
|
int board_fix_fdt(void *fdt_blob)
|
|
{
|
|
debug("Enable SFP Port\n");
|
|
|
|
return fdt_enable_by_ofname(fdt_blob, "/soc/internal-regs/ethernet@30000");
|
|
}
|
|
|
|
#endif
|
|
|
|
int pcie_lane_by_slot(int slot)
|
|
{
|
|
int serdes;
|
|
switch (slot) {
|
|
case 0 :
|
|
serdes = 3;
|
|
break;
|
|
case 1 :
|
|
serdes = 4;
|
|
break;
|
|
case 2 :
|
|
serdes = 1;
|
|
break;
|
|
default :
|
|
serdes = -1;
|
|
break;
|
|
}
|
|
|
|
if ((serdes<0) || serdes>=ARRAY_SIZE(board_serdes_map))
|
|
return -1;
|
|
|
|
switch (board_serdes_map[serdes].serdes_type)
|
|
{
|
|
case PEX0 :
|
|
return 0;
|
|
case PEX1 :
|
|
return 1;
|
|
case PEX2 :
|
|
return 2;
|
|
case PEX3 :
|
|
return 3;
|
|
default :
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static void ft_enable_node(void* blob, const char* name)
|
|
{
|
|
int node_ofs = -1;
|
|
|
|
node_ofs = fdt_path_offset(blob, name);
|
|
if (node_ofs >= 0) {
|
|
fdt_setprop_string(blob, node_ofs, "status", "okay");
|
|
}
|
|
}
|
|
|
|
static void ft_eth1_sgmii0(void *blob)
|
|
{
|
|
printf("FT: enable eth phy on sgmii0\n");
|
|
ft_enable_node(blob, "/soc/internal-regs/ethernet_phy@70000");
|
|
}
|
|
|
|
static void ft_dsa_sgmii0(void *blob)
|
|
{
|
|
printf("FT: enable dsa on sgmii0\n");
|
|
ft_enable_node(blob, "/soc/internal-regs/ethernet_dsa@70000");
|
|
ft_enable_node(blob, "/dsa_eth0@0");
|
|
}
|
|
|
|
static void ft_dsa_sgmii1(void *blob)
|
|
{
|
|
printf("FT: enable dsa on sgmii1\n");
|
|
ft_enable_node(blob, "/soc/internal-regs/ethernet_dsa@30000");
|
|
ft_enable_node(blob, "/dsa_eth1@0");
|
|
}
|
|
|
|
static void ft_sfp_sgmii1(void *blob)
|
|
{
|
|
/* Depending if the second ethernet port is in use
|
|
or not the sfp has a different interface name.
|
|
So enable the proper one. */
|
|
if (board_serdes_map[0].serdes_type==SGMII0) {
|
|
printf("FT: enable sfp for cfg1\n");
|
|
ft_enable_node(blob, "/soc/internal-regs/ethernet_sfp_cfg1@30000");
|
|
} else {
|
|
printf("FT: enable sfp for cfg0\n");
|
|
ft_enable_node(blob, "/soc/internal-regs/ethernet_sfp_cfg0@30000");
|
|
}
|
|
}
|
|
|
|
int ft_board_setup(void *blob, bd_t *bd)
|
|
{
|
|
struct serdes_map* sm;
|
|
u8 sm_count;
|
|
|
|
/* Enabled all components in dts depending on
|
|
current serdes configuration */
|
|
|
|
/* Determine SERDES configuration */
|
|
hws_board_topology_load(&sm, &sm_count);
|
|
|
|
/* Second ethernet port (SERDES0) can only be
|
|
connected to SGMII0. SO check, if we need to enable it. */
|
|
switch (board_serdes_map[0].serdes_type) {
|
|
case SGMII0 :
|
|
ft_eth1_sgmii0(blob);
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
|
|
/* The PoE switch (SERDES1) can be connected either to
|
|
SGMII0 or SGMII1. So check, if we need to enable one of those. */
|
|
switch (board_serdes_map[1].serdes_type) {
|
|
case SGMII0 :
|
|
ft_dsa_sgmii0(blob);
|
|
break;
|
|
case SGMII1 :
|
|
ft_dsa_sgmii1(blob);
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
|
|
/* SFP (SERDES2) can only be connected to SGMII1. So check,
|
|
if we need to enable it. */
|
|
switch (board_serdes_map[2].serdes_type) {
|
|
case SGMII1 :
|
|
ft_sfp_sgmii1(blob);
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|