summaryrefslogtreecommitdiff
path: root/board/toradex/common/board.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/toradex/common/board.c')
-rw-r--r--board/toradex/common/board.c911
1 files changed, 911 insertions, 0 deletions
diff --git a/board/toradex/common/board.c b/board/toradex/common/board.c
new file mode 100644
index 0000000000..b4461699b7
--- /dev/null
+++ b/board/toradex/common/board.c
@@ -0,0 +1,911 @@
+/*
+ * (C) Copyright 2012
+ * Toradex, Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h> /* do not change order of include file */
+#include <malloc.h>
+#include <nand.h>
+#include <ns16550.h>
+#include <watchdog.h>
+#include <asm/clocks.h>
+#include <asm/io.h>
+
+#include <asm/arch-tegra/bitfield.h>
+#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch-tegra/pmc.h>
+#include <asm/arch-tegra/uart.h>
+#include <asm/arch-tegra/warmboot.h>
+#include <asm/arch/clock.h>
+#ifdef CONFIG_TEGRA2
+#include <asm/arch/emc.h>
+#include <asm/arch/gpio.h>
+#endif
+#include <asm/arch/pinmux.h>
+#ifdef CONFIG_TEGRA3
+#include <asm/arch/pmu.h>
+#include <asm/arch/pmu_core.h>
+#endif
+#include <asm/arch/sys_proto.h>
+#ifdef CONFIG_USB_EHCI_TEGRA
+#include <asm/arch/usb.h>
+#endif
+#include <asm/arch/tegra.h>
+
+#ifdef CONFIG_TEGRA_I2C
+#include <i2c.h>
+#endif
+
+#include "board.h"
+#include "../../nvidia/common/pmu.h"
+
+#ifdef CONFIG_TEGRA_MMC
+#include <asm/arch/pmu.h>
+#include <mmc.h>
+#endif
+
+#include <fdt_decode.h>
+#include <libfdt.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+
+#define NV_ADDRESS_MAP_FUSE_BASE 0x7000f800
+// Register FUSE_BOOT_DEVICE_INFO_0
+#define FUSE_BOOT_DEVICE_INFO_0 0x1bc
+// Register FUSE_RESERVED_SW_0
+#define FUSE_RESERVED_SW_0 0x1c0
+
+#define CLK_RST_BASE 0x60006000
+// Register CLK_RST_CONTROLLER_MISC_CLK_ENB_0
+#define CLK_RST_CONTROLLER_MISC_CLK_ENB_0 0x48
+
+// Register APB_MISC_PP_STRAPPING_OPT_A_0
+#define APB_MISC_PP_STRAPPING_OPT_A_0 0x8
+
+typedef enum
+{
+ NvBootFuseBootDevice_Sdmmc,
+ NvBootFuseBootDevice_SnorFlash,
+ NvBootFuseBootDevice_SpiFlash,
+ NvBootFuseBootDevice_NandFlash,
+ NvBootFuseBootDevice_NandFlash_x8 = NvBootFuseBootDevice_NandFlash,
+ NvBootFuseBootDevice_NandFlash_x16 = NvBootFuseBootDevice_NandFlash,
+ NvBootFuseBootDevice_MobileLbaNand,
+ NvBootFuseBootDevice_MuxOneNand,
+ NvBootFuseBootDevice_Sata,
+ NvBootFuseBootDevice_BootRom_Reserved_Sdmmc3, /* !!! this enum is strictly used by BootRom code only !!! */
+ NvBootFuseBootDevice_Max, /* Must appear after the last legal item */
+ NvBootFuseBootDevice_Force32 = 0x7fffffff
+} NvBootFuseBootDevice;
+typedef enum
+{
+ NvStrapDevSel_Emmc_Primary_x4 = 0, /* eMMC primary (x4) */
+ NvStrapDevSel_Emmc_Primary_x8, /* eMMC primary (x8) */
+ NvStrapDevSel_Emmc_Secondary_x4, /* eMMC secondary (x4) */
+ NvStrapDevSel_Nand, /* NAND (x8 or x16) */
+ NvStrapDevSel_Nand_42nm_x8, /* NAND_42nm (x8) */
+ NvStrapDevSel_MobileLbaNand, /* mobileLBA NAND */
+ NvStrapDevSel_MuxOneNand, /* MuxOneNAND */
+ NvStrapDevSel_Esd_x4, /* eSD (x4) */
+ NvStrapDevSel_SpiFlash, /* SPI Flash */
+ NvStrapDevSel_Snor_Muxed_x16, /* Sync NOR (Muxed, x16) */
+ NvStrapDevSel_Snor_Muxed_x32, /* Sync NOR (Muxed, x32) */
+ NvStrapDevSel_Snor_NonMuxed_x16, /* Sync NOR (NonMuxed, x16) */
+ NvStrapDevSel_FlexMuxOneNand, /* FlexMuxOneNAND */
+ NvStrapDevSel_Sata, /* Sata */
+ NvStrapDevSel_Emmc_Secondary_x8, /* eMMC secondary (x8) */
+ NvStrapDevSel_UseFuses, /* Use fuses instead */
+
+ /* The following definitions must be last. */
+ NvStrapDevSel_Num, /* Must appear after the last legal item */
+ NvStrapDevSel_Force32 = 0x7fffffff
+} NvStrapDevSel;
+typedef enum
+{
+ Trdx_BootDevice_NandFlash,
+ Trdx_BootDevice_eMMC,
+ Trdx_BootDevice_extSD,
+ Trdx_BootDevice_Unknown,
+ Trdx_BootDevice_Max, /* Must appear after the last legal item */
+ Trdx_BootDevice_Force32 = 0x7fffffff
+} TrdxBootDevice;
+const char* sTrdxBootDeviceStr[] =
+{
+ "Trdx_BootDevice_NandFlash",
+ "Trdx_BootDevice_eMMC",
+ "Trdx_BootDevice_extSD",
+ "Trdx_BootDevice_Unknown"
+};
+
+
+
+#if defined(CONFIG_TEGRA_CLOCK_SCALING) && !defined(CONFIG_TEGRA_I2C)
+#error "tegra: We need CONFIG_TEGRA_I2C to support CONFIG_TEGRA_CLOCK_SCALING"
+#endif
+
+#if defined(CONFIG_TEGRA_CLOCK_SCALING) && !defined(CONFIG_TEGRA_PMU)
+#error "tegra: We need CONFIG_TEGRA_PMU to support CONFIG_TEGRA_CLOCK_SCALING"
+#endif
+
+#define GENERATE_FUSE_DEV_INFO 0
+static TrdxBootDevice board_get_current_bootdev(void)
+{
+ unsigned reg;
+#if GENERATE_FUSE_DEV_INFO
+ unsigned reg1 = 0;
+ unsigned reg2;
+#endif
+ unsigned strap_select;
+ unsigned skip_strap;
+ unsigned fuse_select;
+#if GENERATE_FUSE_DEV_INFO
+ unsigned fuse_device_info;
+ unsigned sdmmc_instance;
+#endif
+ TrdxBootDevice boot_device;
+
+ //get the latched strap pins, bit [26:29]
+ reg = readl( ((unsigned *)NV_APB_MISC_BASE) + APB_MISC_PP_STRAPPING_OPT_A_0);
+/*
+ printf("Strappings Reg 0x%x, BootSelect 0x%x, Recovery %x, NorBoot %x, JTAG %x, MIO_WIDTH %x, RAM_Code %x, NOR_Width %x\n",
+ reg, (reg>>26)&0xf, (reg>>25)&0x1, (reg>>24)&0x1, (reg>>22)&0x3, (reg>>8)&0x1, (reg>>4)&0xf, (reg)&0x1);
+*/
+ strap_select = (reg>>26)&0xf;
+
+#if 0 //Max: this does not work, there is more to read fuses than that
+ //check if we can access BIT /tegra/core/include/nvbit.h et.al.
+
+
+ clock_enable(PERIPH_ID_FUSE);
+ //make fuses visible
+ reg = readl( ((unsigned *)CLK_RST_BASE) + CLK_RST_CONTROLLER_MISC_CLK_ENB_0);
+ reg = reg | (1<<28);
+ writel(reg, ((unsigned *)CLK_RST_BASE) + CLK_RST_CONTROLLER_MISC_CLK_ENB_0 );
+
+ printf("1");
+ reg = readl( ((unsigned *)NV_ADDRESS_MAP_FUSE_BASE) + FUSE_RESERVED_SW_0);
+ printf("2");
+ reg1 = readl( ((unsigned *)NV_ADDRESS_MAP_FUSE_BASE) + FUSE_BOOT_DEVICE_INFO_0);
+ printf("3");
+
+ //make fuses invisible
+ reg2 = readl( ((unsigned *)CLK_RST_BASE) + CLK_RST_CONTROLLER_MISC_CLK_ENB_0);
+ reg2 = reg2 & ~(1<<28);
+ writel(reg2, ((unsigned *)CLK_RST_BASE) + CLK_RST_CONTROLLER_MISC_CLK_ENB_0 );
+
+ clock_disable(PERIPH_ID_FUSE);
+#else
+#ifdef CONFIG_TEGRA3
+ //simulate a T30 fuse setting
+ reg = NvBootFuseBootDevice_Sdmmc;
+#else
+ //simulate a T20 fuse setting
+ reg = NvBootFuseBootDevice_NandFlash;
+#endif
+#endif
+ //get the fuse 'SKIP_DEV_SEL_STRAPS', bit 3
+ skip_strap = (reg & 8)>>3;
+ //get the fuse 'BOOT_DEV_SEL', bit [0:2]
+ fuse_select = reg & 7;
+
+ if(skip_strap || strap_select == NvStrapDevSel_UseFuses)
+ {
+ printf("Using fuses, %u\n", fuse_select);
+ //getting fuse device info and sdmmc instance, bit 7 of fuse_device info
+#if GENERATE_FUSE_DEV_INFO
+ fuse_device_info = reg1 & 0x3fff;
+ sdmmc_instance = ((reg1 & 0x80)==0x80) ? 2 : 3;
+#endif
+ switch(fuse_select)
+ {
+ case NvBootFuseBootDevice_Sdmmc:
+ boot_device = Trdx_BootDevice_eMMC;
+ break;
+ case NvBootFuseBootDevice_NandFlash:
+ boot_device = Trdx_BootDevice_NandFlash;
+ break;
+ default:
+ boot_device = Trdx_BootDevice_Unknown;
+ break;
+ }
+ }
+ else
+ {
+ /* printf("Using straps, %u\n", strap_select);*/
+#if GENERATE_FUSE_DEV_INFO
+ sdmmc_instance = 3;
+#endif
+ switch(strap_select)
+ {
+ case NvStrapDevSel_Emmc_Primary_x4:
+ case NvStrapDevSel_Emmc_Secondary_x4:
+ case NvStrapDevSel_Emmc_Secondary_x8:
+ case NvStrapDevSel_Esd_x4:
+ boot_device = Trdx_BootDevice_extSD;
+ break;
+ case NvStrapDevSel_Nand:
+ case NvStrapDevSel_Nand_42nm_x8:
+ boot_device = Trdx_BootDevice_NandFlash;
+ break;
+ default:
+ boot_device = Trdx_BootDevice_Unknown;
+ break;
+ }
+ }
+ /*
+ if(boot_device < sizeof(sTrdxBootDeviceStr)/sizeof(sTrdxBootDeviceStr[0]))
+ {
+ printf("%s is used\n",sTrdxBootDeviceStr[boot_device]);
+ }
+ */
+ return boot_device;
+}
+
+static void board_voltage_init(void);
+
+enum {
+ /* UARTs which we can enable */
+ UARTA = 1 << 0,
+ UARTB = 1 << 1,
+ UARTD = 1 << 3,
+ UART_ALL = 0xf
+};
+
+#if defined(BOARD_LATE_INIT) && (defined(CONFIG_TRDX_CFG_BLOCK_OFFSET) || \
+ defined(CONFIG_REVISION_TAG) || defined(CONFIG_SERIAL_TAG))
+static unsigned char *config_block = NULL;
+#endif
+
+#ifdef CONFIG_HW_WATCHDOG
+static int i2c_is_initialized = 0;
+#endif
+
+/*
+ * Routine: timer_init
+ * Description: init the timestamp and lastinc value
+ */
+int timer_init(void)
+{
+ reset_timer();
+ return 0;
+}
+
+static void enable_uart(enum periph_id pid)
+{
+ /* Assert UART reset and enable clock */
+ reset_set_enable(pid, 1);
+ clock_enable(pid);
+ clock_ll_set_source(pid, 0); /* UARTx_CLK_SRC = 00, PLLP_OUT0 */
+
+ /* wait for 2us */
+ udelay(2);
+
+ /* De-assert reset to UART */
+ reset_set_enable(pid, 0);
+}
+
+/*
+ * Routine: clock_init_uart
+ * Description: init clock for the UART(s)
+ */
+static void clock_init_uart(int uart_ids)
+{
+ if (uart_ids & UARTA)
+ enable_uart(PERIPH_ID_UART1);
+ if (uart_ids & UARTB)
+ enable_uart(PERIPH_ID_UART2);
+ if (uart_ids & UARTD)
+ enable_uart(PERIPH_ID_UART4);
+}
+
+/*
+ * Routine: pin_mux_uart
+ * Description: setup the pin muxes/tristate values for the UART(s)
+ */
+static void pin_mux_uart(int uart_ids)
+{
+#if defined(CONFIG_TEGRA2)
+ if (uart_ids & UARTA) {
+ /* Disable UART1 where primary function */
+ pinmux_tristate_enable(PINGRP_IRRX);
+ pinmux_tristate_enable(PINGRP_IRTX);
+ pinmux_set_func(PINGRP_IRRX, PMUX_FUNC_GMI);
+ pinmux_set_func(PINGRP_IRTX, PMUX_FUNC_GMI);
+ pinmux_tristate_enable(PINGRP_SDB);
+ pinmux_tristate_enable(PINGRP_SDD);
+ pinmux_set_func(PINGRP_SDB, PMUX_FUNC_PWM);
+ pinmux_set_func(PINGRP_SDD, PMUX_FUNC_PWM);
+
+ pinmux_set_func(PINGRP_SDMMC1, PMUX_FUNC_UARTA);
+ pinmux_tristate_disable(PINGRP_SDMMC1);
+ }
+ if (uart_ids & UARTB) {
+ pinmux_set_func(PINGRP_UAD, PMUX_FUNC_IRDA);
+ pinmux_tristate_disable(PINGRP_UAD);
+ }
+ if (uart_ids & UARTD) {
+ pinmux_set_func(PINGRP_GMC, PMUX_FUNC_UARTD);
+ pinmux_tristate_disable(PINGRP_GMC);
+ }
+#endif /* CONFIG_TEGRA2 */
+}
+
+#ifdef CONFIG_TEGRA_MMC
+/*
+ * Routine: pin_mux_mmc
+ * Description: setup the pin muxes/tristate values for the SDMMC(s)
+ */
+static void pin_mux_mmc(void)
+{
+#ifdef CONFIG_TEGRA2
+ /* SDMMC4: config 3, x4 on 2nd set of pins */
+ pinmux_set_func(PINGRP_ATB, PMUX_FUNC_SDIO4);
+ pinmux_set_func(PINGRP_GMA, PMUX_FUNC_SDIO4);
+
+ pinmux_tristate_disable(PINGRP_ATB);
+ pinmux_tristate_disable(PINGRP_GMA);
+#endif
+}
+#endif
+
+#ifdef CONFIG_TEGRA3
+#include "../colibri_t30/pinmux-config-common.h"
+#endif
+
+/*
+ * Routine: pinmux_init
+ * Description: Do individual peripheral pinmux configs
+ */
+#if defined(CONFIG_TEGRA3)
+static void pinmux_init(void)
+{
+ pinmux_config_table(tegra3_pinmux_common,
+ ARRAY_SIZE(tegra3_pinmux_common));
+
+ pinmux_config_table(unused_pins_lowpower,
+ ARRAY_SIZE(unused_pins_lowpower));
+}
+#endif
+
+static void init_uarts(const void *blob)
+{
+ int uart_ids = 0; /* bit mask of which UART ids to enable */
+ struct fdt_uart uart;
+
+ if (!fdt_decode_uart_console(blob, &uart, gd->baudrate))
+ uart_ids = 1 << uart.id;
+
+ /* Initialize UART clocks */
+ clock_init_uart(uart_ids);
+
+ /* Initialize periph pinmuxes */
+#if defined(CONFIG_TEGRA2)
+ pin_mux_uart(uart_ids);
+#endif
+}
+
+/*
+ * Routine: power_det_init
+ * Description: turn off power detects
+ */
+static void power_det_init(void)
+{
+#if defined(CONFIG_TEGRA2)
+ struct pmc_ctlr *const pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+
+ /* turn off power detects */
+ writel(0, &pmc->pmc_pwr_det_latch);
+ writel(0, &pmc->pmc_pwr_det);
+#endif
+}
+
+#if defined(CONFIG_TEGRA3)
+static void enable_clock(enum periph_id pid, int src)
+{
+ /* Assert reset and enable clock */
+ reset_set_enable(pid, 1);
+ clock_enable(pid);
+
+ /* Use 'src' if provided, else use default */
+ if (src != -1)
+ clock_ll_set_source(pid, src);
+
+ /* wait for 2us */
+ udelay(2);
+
+ /* De-assert reset */
+ reset_set_enable(pid, 0);
+}
+
+/* Init misc clocks for kernel booting */
+static void clock_init_misc(void)
+{
+ /* 0 = PLLA_OUT0, -1 = CLK_M (default) */
+ enable_clock(PERIPH_ID_I2S0, -1);
+ enable_clock(PERIPH_ID_I2S1, 0);
+ enable_clock(PERIPH_ID_I2S2, 0);
+ enable_clock(PERIPH_ID_I2S3, 0);
+ enable_clock(PERIPH_ID_I2S4, -1);
+ enable_clock(PERIPH_ID_SPDIF, -1);
+}
+#endif
+
+/*
+ * Routine: board_init
+ * Description: Early hardware init.
+ */
+int board_init(void)
+{
+#ifdef CONFIG_VIDEO_TEGRA
+ tegra_lcd_check_next_stage(gd->blob, 0);
+#endif
+
+#ifdef CONFIG_DELAY_CONSOLE
+ init_uarts(gd->blob);
+#endif
+
+ /* Do clocks and UART first so that printf() works */
+ clock_init();
+
+#ifdef CONFIG_USB_EHCI_TEGRA
+ board_usb_init(gd->blob);
+#endif
+
+ clock_verify();
+
+ power_det_init();
+
+ (void)board_get_current_bootdev();
+
+#ifdef CONFIG_TEGRA_I2C
+ /* Ramp up the core voltage, then change to full CPU speed */
+ i2c_init_board();
+#endif
+
+#ifdef CONFIG_TEGRA_CLOCK_SCALING
+ pmu_set_nominal();
+ arch_full_speed();
+#endif
+
+ /* board id for Linux */
+ gd->bd->bi_arch_number = fdt_decode_get_machine_arch_id(gd->blob);
+ if (gd->bd->bi_arch_number == -1U)
+ printf("Warning: No /config/machine-arch-id defined in fdt\n");
+
+#ifdef CONFIG_TEGRA_CLOCK_SCALING
+ board_emc_init();
+#endif
+
+ board_voltage_init();
+
+#ifdef CONFIG_HW_WATCHDOG
+ i2c_is_initialized = 1;
+#endif
+
+#ifdef CONFIG_TEGRA_LP0
+ /* prepare the WB code to LP0 location */
+//ToDo: determine LP0 address dynamically
+ warmboot_prepare_code(TEGRA_LP0_ADDR, TEGRA_LP0_SIZE);
+#endif
+
+ /* boot param addr */
+ gd->bd->bi_boot_params = (NV_PA_SDRAM_BASE + 0x100);
+
+ return 0;
+}
+
+#ifdef CONFIG_BOARD_EARLY_INIT_F
+int board_early_init_f(void)
+{
+ ulong pllp_rate = 216000000; /* default PLLP clock rate */
+
+ /* Initialize essential common plls */
+ pllp_rate = fdt_decode_clock_rate(gd->blob, "pllp", pllp_rate);
+ clock_early_init(pllp_rate);
+
+#ifdef CONFIG_TEGRA3
+ pinmux_init();
+#endif
+
+#ifndef CONFIG_DELAY_CONSOLE
+ init_uarts(gd->blob);
+#endif
+
+#if defined(CONFIG_TEGRA3)
+ /* Initialize misc clocks for kernel booting */
+ clock_init_misc();
+#endif
+
+#ifdef CONFIG_VIDEO_TEGRA
+ /* Get LCD panel size */
+ lcd_early_init(gd->blob);
+#endif
+
+ return 0;
+}
+#endif /* EARLY_INIT */
+
+#ifdef BOARD_LATE_INIT
+int board_late_init(void)
+{
+ char env_str[256 ];
+
+#ifdef CONFIG_TRDX_CFG_BLOCK_OFFSET
+ char *addr_str, *end;
+ unsigned char bi_enetaddr[6] = {0, 0, 0, 0, 0, 0}; /* Ethernet address */
+ int i;
+ unsigned char *mac_addr;
+ unsigned char mac_addr00[6] = {0, 0, 0, 0, 0, 0};
+
+ size_t size = 4096;
+ unsigned char toradex_oui[3] = { 0x00, 0x14, 0x2d };
+ int valid = 0;
+
+ unsigned int offset;
+ int ret;
+#endif /* CONFIG_TRDX_CFG_BLOCK_OFFSET */
+
+#ifdef CONFIG_VIDEO_TEGRA
+ /* Make sure we finish initing the LCD */
+ tegra_lcd_check_next_stage(gd->blob, 1);
+#endif
+
+#ifdef CONFIG_TRDX_CFG_BLOCK_OFFSET
+ /* Allocate RAM area for config block */
+ config_block = malloc(size);
+ if (!config_block) {
+ printf("Not enough malloc space available!\n");
+ return -1;
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (i == 0)
+ offset = CONFIG_TRDX_CFG_BLOCK_OFFSET;
+ else
+ offset = CONFIG_TRDX_CFG_BLOCK_OFFSET2;
+
+ /* Clear it */
+ memset((void *)config_block, 0, size);
+
+ /* Read production parameter config block */
+#ifdef CONFIG_CMD_NAND
+ ret = nand_read_skip_bad(&nand_info[0], offset, &size, (unsigned char *) config_block);
+#endif
+ /* Check validity */
+ if ( (ret==0) && (size>0) ) {
+ mac_addr = config_block + 8;
+ if (!(memcmp(mac_addr, toradex_oui, 3))) {
+ valid = 1;
+ break;
+ }
+ }
+ else {
+ /* probably there is no flash,
+ * give up reading the config block, the nand flash layer crashes on the second attempt */
+ break;
+ }
+ }
+
+ /* Check validity */
+ if (!valid) {
+ printf("Missing Colibri config block\n");
+ memset((void *)config_block, 0, size);
+ } else {
+ /* Get MAC address from environment */
+ if ((addr_str = getenv("ethaddr")) != NULL) {
+ for (i = 0; i < 6; i++) {
+ bi_enetaddr[i] = addr_str ? simple_strtoul(addr_str, &end, 16) : 0;
+ if (addr_str) {
+ addr_str = (*end) ? end + 1 : end;
+ }
+ }
+ }
+
+ /* Set Ethernet MAC address from config block if not already set */
+ if (memcmp(mac_addr00, bi_enetaddr, 6) == 0) {
+ sprintf(env_str, "%02x:%02x:%02x:%02x:%02x:%02x",
+ mac_addr[0], mac_addr[1], mac_addr[2],
+ mac_addr[3], mac_addr[4], mac_addr[5]);
+ setenv("ethaddr", env_str);
+#ifndef CONFIG_ENV_IS_NOWHERE
+ saveenv();
+#endif
+ }
+ }
+
+ /* Default memory arguments */
+ if (!getenv("memargs")) {
+ if (gd->ram_size == 0x10000000) {
+ /* 256 MB */
+ setenv("memargs", "mem=148M@0M fbmem=12M@148M nvmem=96M@160M");
+ } else {
+ /* 512 MB */
+ setenv("memargs", "mem=372M@0M fbmem=12M@372M nvmem=128M@384M");
+ }
+ }
+
+#endif /* CONFIG_TRDX_CFG_BLOCK_OFFSET */
+
+ /* set the nand kernel offset */
+ if (!getenv("lnxoffset")) {
+ sprintf(env_str, "0x%x", (unsigned)(gd->kernel_offset));
+ setenv("lnxoffset", env_str);
+ }
+
+ /* set the mtdparts string */
+ if (!getenv("mtdparts")) {
+ sprintf(env_str, "mtdparts=tegra_nand:%uK@%uK(userspace)",
+ (unsigned)(gd->rootfs_size), (unsigned)(gd->rootfs_offset) );
+ setenv("mtdparts", env_str);
+ }
+
+ return 0;
+}
+#endif /* BOARD_LATE_INIT */
+
+#ifdef CONFIG_TEGRA_MMC
+/* this is a weak define that we are overriding */
+int board_mmc_init(bd_t *bd)
+{
+ debug("board_mmc_init called\n");
+
+ /* Enable muxes, etc. for SDMMC controllers */
+ pin_mux_mmc();
+
+ tegra_mmc_init(gd->blob);
+
+ return 0;
+}
+#endif
+
+/*
+ * MK: Do I2C/PMU writes to set nominal voltages for Colibri_T30
+ *
+ */
+static void board_voltage_init(void)
+{
+#if defined(CONFIG_TEGRA3)
+ uchar reg, data_buffer[1];
+ int i;
+
+ i2c_set_bus_num(0); /* PMU is on bus 0 */
+
+ //switch v-ddr ram to 1.35V
+ data_buffer[0] = 0x3f;
+ reg = 0x25;
+
+ for (i = 0; i < MAX_I2C_RETRY; ++i) {
+ if (i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1))
+ udelay(100);
+ }
+
+ //switch v-core to 1.2V
+ data_buffer[0] = VDD_CORE_NOMINAL_T30;
+ reg = PMU_CORE_VOLTAGE_START_REG;
+
+ for (i = 0; i < MAX_I2C_RETRY; ++i) {
+ if (i2c_write(PMU_CORE_I2C_ADDRESS, reg, 1, data_buffer, 1))
+ udelay(100);
+ }
+ data_buffer[0] = VDD_CORE_NOMINAL_T30;
+ reg = PMU_CORE_VOLTAGE_DVFS_REG;
+ for (i = 0; i < MAX_I2C_RETRY; ++i) {
+ if (i2c_write(PMU_CORE_I2C_ADDRESS, reg, 1, data_buffer, 1))
+ udelay(100);
+ }
+
+#ifndef CONFIG_HW_WATCHDOG
+ //disable watchdog
+ data_buffer[0] = 0x10; /* kick the dog once before disabling it or disabling will fail */
+ reg = 0x54;
+ for (i = 0; i < MAX_I2C_RETRY; ++i) {
+ if (i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1))
+ udelay(100);
+ }
+
+ data_buffer[0] = 0xf0; /* at least one of the reserved bits must be '1' or disabling will fail */
+ reg = 0x69;
+ for (i = 0; i < MAX_I2C_RETRY; ++i) {
+ if (i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1))
+ udelay(100);
+ }
+#endif
+#endif
+}
+
+#ifdef CONFIG_REVISION_TAG
+u32 get_board_rev(void)
+{
+#ifdef BOARD_LATE_INIT
+ int i;
+ unsigned short major = 0, minor = 0, release = 0;
+ size_t size = 4096;
+
+ if(config_block == NULL) {
+ return 0;
+ }
+
+ /* Parse revision information in config block */
+ for (i = 0; i < (size - 8); i++) {
+ if (config_block[i] == 0x02 && config_block[i+1] == 0x40 &&
+ config_block[i+2] == 0x08) {
+ break;
+ }
+ }
+
+ major = (config_block[i+3] << 8) | config_block[i+4];
+ minor = (config_block[i+5] << 8) | config_block[i+6];
+ release = (config_block[i+7] << 8) | config_block[i+8];
+
+ /* Check validity */
+ if (major)
+ return ((major & 0xff) << 8) | ((minor & 0xf) << 4) | ((release & 0xf) + 0xa);
+ else
+ return 0;
+#else
+ return 0;
+#endif /* BOARD_LATE_INIT */
+}
+#endif /* CONFIG_REVISION_TAG */
+
+#ifdef CONFIG_SERIAL_TAG
+void get_board_serial(struct tag_serialnr *serialnr)
+{
+#ifdef BOARD_LATE_INIT
+ int array[8];
+ int i;
+ unsigned int serial = 0;
+ unsigned int serial_offset = 11;
+
+ if(config_block == NULL) {
+ serialnr->low = 0;
+ serialnr->high = 0;
+ return;
+ }
+
+ /* Get MAC address from config block */
+ memcpy(&serial, config_block + serial_offset, 3);
+ serial = ntohl(serial);
+ serial >>= 8;
+
+ /* Check validity */
+ if (serial) {
+ /* Convert to Linux serial number format (hexadecimal coded decimal) */
+ i = 7;
+ while (serial) {
+ array[i--] = serial % 10;
+ serial /= 10;
+ }
+ serial = 0;
+ for (i = 0; i < 8; i++) {
+ serial += array[i];
+ serial *= 16;
+ }
+ serial /= 16;
+ }
+
+ serialnr->low = serial;
+#else
+ serialnr->low = 0;
+#endif /* BOARD_LATE_INIT */
+ serialnr->high = 0;
+}
+#endif /* CONFIG_SERIAL_TAG */
+
+/*
+ * Possible UART locations: we ignore UARTC at 0x70006200 and UARTE at
+ * 0x70006400, since we don't have code to init them
+ */
+static u32 uart_reg_addr[] = {
+ NV_PA_APB_UARTA_BASE,
+ NV_PA_APB_UARTB_BASE,
+ NV_PA_APB_UARTD_BASE,
+ 0
+};
+
+#ifdef CONFIG_HW_WATCHDOG
+/*
+ * kick watchdog in PMU
+ *
+ */
+extern void hw_watchdog_reset(void)
+{
+#if defined(CONFIG_TEGRA3)
+ uchar reg, data_buffer[1];
+ static unsigned long last_kick = 0;
+ unsigned long now;
+ // only kick the watchdog every 10 seconds, the watchdog timeout is 100s
+ now = get_ticks();
+ //first kick after 10 seconds
+ if( !i2c_is_initialized || (last_kick == 0) ) {
+ last_kick = now;
+ }
+ else if( (signed)(now - last_kick) > (10 * 1000) ) {
+ last_kick = now;
+ data_buffer[0] = 0x10;
+ reg = 0x54;
+ i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1);
+ }
+#endif
+}
+#endif
+
+/**
+ * Send out serial output wherever we can.
+ *
+ * This function produces a low-level panic message, after setting PLLP
+ * to the given value.
+ *
+ * @param pllp_rate Required PLLP rate (408000000 or 216000000)
+ * @param str String to output
+ */
+static void send_output_with_pllp(ulong pllp_rate, const char *str)
+{
+ int uart_ids = UART_ALL; /* turn it all on! */
+ u32 *uart_addr;
+ int clock_freq, multiplier, baudrate, divisor;
+
+ clock_early_init(pllp_rate);
+
+ /* Try to enable all possible UARTs */
+ clock_init_uart(uart_ids);
+ pin_mux_uart(uart_ids);
+
+#ifdef CONFIG_TEGRA3
+ /* Until we sort out pinmux, we must do the global Tegra3 init */
+ pinmux_init();
+#endif
+
+ /*
+ * Now send the string out all the Tegra UARTs. We don't try all
+ * possible configurations, but this could be added if required.
+ */
+ clock_freq = pllp_rate;
+ multiplier = CONFIG_DEFAULT_NS16550_MULT;
+ baudrate = CONFIG_BAUDRATE;
+ divisor = (clock_freq + (baudrate * (multiplier / 2))) /
+ (multiplier * baudrate);
+
+ for (uart_addr = uart_reg_addr; *uart_addr; uart_addr++) {
+ const char *s;
+
+ NS16550_init((NS16550_t)*uart_addr, divisor);
+ for (s = str; *s; s++) {
+ NS16550_putc((NS16550_t)*uart_addr, *s);
+ if (*s == '\n')
+ NS16550_putc((NS16550_t)*uart_addr, '\r');
+ }
+ }
+}
+
+/*
+ * This is called when we have no console. About the only reason that this
+ * happen is if we don't have a valid fdt. So we don't know what kind of
+ * Tegra board we are. We blindly try to print a message every which way we
+ * know.
+ */
+void board_panic_no_console(const char *str)
+{
+ /* We don't know what PLLP to use, so try both */
+ send_output_with_pllp(216000000, str);
+ send_output_with_pllp(408000000, str);
+}