summaryrefslogtreecommitdiff
path: root/board/toradex/common
diff options
context:
space:
mode:
Diffstat (limited to 'board/toradex/common')
-rw-r--r--board/toradex/common/Makefile60
-rw-r--r--board/toradex/common/board.c911
-rw-r--r--board/toradex/common/board.h52
-rw-r--r--board/toradex/common/tegra2_nand.c1095
-rw-r--r--board/toradex/common/tegra2_nand.h280
-rw-r--r--board/toradex/common/ulpi_linux.c194
-rw-r--r--board/toradex/common/usb.c561
7 files changed, 3153 insertions, 0 deletions
diff --git a/board/toradex/common/Makefile b/board/toradex/common/Makefile
new file mode 100644
index 0000000000..e3a6dbf938
--- /dev/null
+++ b/board/toradex/common/Makefile
@@ -0,0 +1,60 @@
+# Copyright (c) 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 $(TOPDIR)/config.mk
+
+ifneq ($(OBJTREE),$(SRCTREE))
+$(shell mkdir -p $(obj)crypto)
+endif
+
+LIB = $(obj)lib$(VENDOR).o
+
+COBJS-y += board.o
+COBJS-$(CONFIG_TEGRA2_NAND) += tegra2_nand.o
+COBJS-$(CONFIG_USB_EHCI_TEGRA) += ulpi_linux.o
+COBJS-$(CONFIG_USB_EHCI_TEGRA) += usb.o
+
+COBJS-$(CONFIG_TEGRA_CLOCK_SCALING) += ../../nvidia/common/emc.o
+COBJS-$(CONFIG_TEGRA_PMU) += ../../nvidia/common/pmu.o
+COBJS-$(CONFIG_TEGRA_LP0) += ../../nvidia/common/crypto/aes_ref.o
+COBJS-$(CONFIG_TEGRA_LP0) += ../../nvidia/common/crypto/crypto.o
+
+COBJS := $(COBJS-y)
+SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+SOBJS := $(addprefix $(obj),$(SOBJS))
+
+all: $(LIB)
+
+$(LIB): $(obj).depend $(OBJS) $(SOBJS)
+ $(call cmd_link_o_target, $(OBJS) $(SOBJS))
+
+clean:
+ rm -f $(SOBJS) $(OBJS)
+
+distclean: clean
+ rm -f $(LIB) core *.bak $(obj).depend
+
+#########################################################################
+# This is for $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
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);
+}
diff --git a/board/toradex/common/board.h b/board/toradex/common/board.h
new file mode 100644
index 0000000000..8e0689d509
--- /dev/null
+++ b/board/toradex/common/board.h
@@ -0,0 +1,52 @@
+/*
+ * (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
+ */
+
+#ifndef _BOARD_H_
+#define _BOARD_H_
+
+void gpio_config_uart(const void *blob);
+void gpio_early_init_uart(const void *blob);
+void gpio_config_mmc(void);
+int tegra_mmc_init(const void *blob);
+void lcd_early_init(const void *blob);
+
+/**
+ * Perform the next stage of the LCD init if it is time to do so.
+ *
+ * LCD init can be time-consuming because of the number of delays we need
+ * while waiting for the backlight power supply, etc. This function can
+ * be called at various times during U-Boot operation to advance the
+ * initialization of the LCD to the next stage if sufficient time has
+ * passed since the last stage. It keeps track of what stage it is up to
+ * and the time that it is permitted to move to the next stage.
+ *
+ * The final call should have wait=1 to complete the init.
+ *
+ * @param blob fdt blob containing LCD information
+ * @param wait 1 to wait until all init is complete, and then return
+ * 0 to return immediately, potentially doing nothing if it is
+ * not yet time for the next init.
+ */
+int tegra_lcd_check_next_stage(const void *blob, int wait);
+
+#endif /* BOARD_H */
diff --git a/board/toradex/common/tegra2_nand.c b/board/toradex/common/tegra2_nand.c
new file mode 100644
index 0000000000..c1eaa8f429
--- /dev/null
+++ b/board/toradex/common/tegra2_nand.c
@@ -0,0 +1,1095 @@
+/*
+ * (C) Copyright 2006 Detlev Zundel, dzu@denx.de
+ * (C) Copyright 2006 DENX Software Engineering
+ * (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com>
+ * (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>
+#include <asm/io.h>
+#include <nand.h>
+#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch/clock.h>
+#include <asm/clocks.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch/gpio.h>
+#include <asm/errno.h>
+#include <fdt_decode.h>
+#include "tegra2_nand.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define NAND_CMD_TIMEOUT_MS 10
+#define SCAN_TIMING_VAL 0x3f0bd214
+#define SCAN_TIMING2_VAL 0xb
+
+static struct nand_ecclayout eccoob = {
+ .eccpos = {
+ 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 60, 61, 62, 63,
+ },
+};
+
+enum {
+ ECC_OK,
+ ECC_TAG_ERROR = 1 << 0,
+ ECC_DATA_ERROR = 1 << 1
+};
+
+struct nand_info {
+ struct nand_ctlr *reg;
+/*
+ * When running in PIO mode to get READ ID bytes from register
+ * RESP_0, we need this variable as an index to know which byte in
+ * register RESP_0 should be read.
+ * Because common code in nand_base.c invokes read_byte function two times
+ * for NAND_CMD_READID.
+ * And our controller returns 4 bytes at once in register RESP_0.
+ */
+ int pio_byte_index;
+ struct fdt_nand config;
+};
+
+struct nand_info nand_ctrl;
+
+/**
+ * nand_waitfor_cmd_completion - wait for command completion
+ * @param reg: nand_ctlr structure
+ * @return:
+ * 1 - Command completed
+ * 0 - Timeout
+ */
+static int nand_waitfor_cmd_completion(struct nand_ctlr *reg)
+{
+ int i;
+ u32 reg_val;
+
+ for (i = 0; i < NAND_CMD_TIMEOUT_MS * 1000; i++) {
+ if ((readl(&reg->command) & CMD_GO) ||
+ !(readl(&reg->status) &
+ STATUS_RBSY0) ||
+ !(readl(&reg->isr) &
+ ISR_IS_CMD_DONE)) {
+ udelay(1);
+ continue;
+ }
+ reg_val = readl(&reg->dma_mst_ctrl);
+ /*
+ * If DMA_MST_CTRL_EN_A_ENABLE or
+ * DMA_MST_CTRL_EN_B_ENABLE is set,
+ * that means DMA engine is running, then we
+ * have to wait until
+ * DMA_MST_CTRL_IS_DMA_DONE
+ * is cleared for DMA transfer completion.
+ */
+ if (reg_val & (DMA_MST_CTRL_EN_A_ENABLE |
+ DMA_MST_CTRL_EN_B_ENABLE)) {
+ if (reg_val & DMA_MST_CTRL_IS_DMA_DONE)
+ return 1;
+ } else
+ return 1;
+ udelay(1);
+ }
+ return 0;
+}
+
+/**
+ * nand_read_byte - [DEFAULT] read one byte from the chip
+ * @param mtd: MTD device structure
+ * @return: data byte
+ *
+ * Default read function for 8bit bus-width
+ */
+static uint8_t nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ int dword_read;
+ struct nand_info *info;
+
+ info = (struct nand_info *) chip->priv;
+
+ dword_read = readl(&info->reg->resp);
+ dword_read = dword_read >> (8 * info->pio_byte_index);
+ info->pio_byte_index++;
+ return (uint8_t) dword_read;
+}
+
+/**
+ * nand_write_buf - [DEFAULT] write buffer to chip
+ * @param mtd: MTD device structure
+ * @param buf: data buffer
+ * @param len: number of bytes to write
+ *
+ * Default write function for 8bit bus-width
+ */
+static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ int i, j, l;
+ struct nand_chip *chip = mtd->priv;
+ struct nand_info *info;
+
+ info = (struct nand_info *) chip->priv;
+
+ for (i = 0; i < len / 4; i++) {
+ l = ((int *)buf)[i];
+ writel(l, &info->reg->resp);
+ writel(CMD_GO | CMD_PIO | CMD_TX |
+ (CMD_TRANS_SIZE_BYTES4 <<
+ CMD_TRANS_SIZE_SHIFT)
+ | CMD_A_VALID | CMD_CE0,
+ &info->reg->command);
+
+ if (!nand_waitfor_cmd_completion(info->reg))
+ printf("Command timeout during write_buf\n");
+ }
+ if (len & 3) {
+ l = 0;
+ for (j = 0; j < (len & 3); j++)
+ l |= (((int) buf[i * 4 + j]) << (8 * j));
+
+ writel(l, &info->reg->resp);
+ writel(CMD_GO | CMD_PIO | CMD_TX |
+ (((len & 3) - 1) << CMD_TRANS_SIZE_SHIFT) |
+ CMD_A_VALID | CMD_CE0,
+ &info->reg->command);
+ if (!nand_waitfor_cmd_completion(info->reg))
+ printf("Command timeout during write_buf\n");
+ }
+}
+
+/**
+ * nand_read_buf - [DEFAULT] read chip data into buffer
+ * @param mtd: MTD device structure
+ * @param buf: buffer to store date
+ * @param len: number of bytes to read
+ *
+ * Default read function for 8bit bus-width
+ */
+static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i, j, l;
+ struct nand_chip *chip = mtd->priv;
+ int *buf_dword;
+ struct nand_info *info;
+
+ info = (struct nand_info *) chip->priv;
+
+ buf_dword = (int *) buf;
+ for (i = 0; i < len / 4; i++) {
+ writel(CMD_GO | CMD_PIO | CMD_RX |
+ (CMD_TRANS_SIZE_BYTES4 <<
+ CMD_TRANS_SIZE_SHIFT)
+ | CMD_A_VALID | CMD_CE0,
+ &info->reg->command);
+ if (!nand_waitfor_cmd_completion(info->reg))
+ printf("Command timeout during read_buf\n");
+ l = readl(&info->reg->resp);
+ buf_dword[i] = l;
+ }
+ if (len & 3) {
+ writel(CMD_GO | CMD_PIO | CMD_RX |
+ (((len & 3) - 1) << CMD_TRANS_SIZE_SHIFT) |
+ CMD_A_VALID | CMD_CE0,
+ &info->reg->command);
+ if (!nand_waitfor_cmd_completion(info->reg))
+ printf("Command timeout during read_buf\n");
+ l = readl(&info->reg->resp);
+ for (j = 0; j < (len & 3); j++)
+ buf[i * 4 + j] = (char) (l >> (8 * j));
+ }
+}
+
+/**
+ * nand_dev_ready - check NAND status is ready or not
+ * @param mtd: MTD device structure
+ * @return:
+ * 1 - ready
+ * 0 - not ready
+ */
+static int nand_dev_ready(struct mtd_info *mtd)
+{
+ register struct nand_chip *chip = mtd->priv;
+ int reg_val;
+ struct nand_info *info;
+
+ info = (struct nand_info *) chip->priv;
+
+ reg_val = readl(&info->reg->status);
+ if (reg_val & STATUS_RBSY0)
+ return 1;
+ else
+ return 0;
+}
+
+/* Hardware specific access to control-lines */
+static void nand_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+}
+
+/**
+ * nand_clear_interrupt_status - clear all interrupt status bits
+ * @param reg: nand_ctlr structure
+ */
+static void nand_clear_interrupt_status(struct nand_ctlr *reg)
+{
+ u32 reg_val;
+
+ /* Clear interrupt status */
+ reg_val = readl(&reg->isr);
+ writel(reg_val, &reg->isr);
+}
+
+/**
+ * nand_command - [DEFAULT] Send command to NAND device
+ * @param mtd: MTD device structure
+ * @param command: the command to be sent
+ * @param column: the column address for this command, -1 if none
+ * @param page_addr: the page address for this command, -1 if none
+ */
+static void nand_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct nand_info *info;
+
+ info = (struct nand_info *) chip->priv;
+
+ /*
+ * Write out the command to the device.
+ */
+ if (mtd->writesize < 2048) {
+ /*
+ * Only command NAND_CMD_RESET or NAND_CMD_READID will come
+ * here before mtd->writesize is initialized, we don't have
+ * any action here because page size of NAND HY27UF084G2B
+ * is 2048 bytes and mtd->writesize will be 2048 after
+ * initialized.
+ */
+ } else {
+ /* Emulate NAND_CMD_READOOB */
+ if (command == NAND_CMD_READOOB) {
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* Adjust columns for 16 bit bus-width */
+ if (column != -1 && (chip->options & NAND_BUSWIDTH_16))
+ column >>= 1;
+ }
+
+ nand_clear_interrupt_status(info->reg);
+
+ /* Stop DMA engine, clear DMA completion status */
+ writel(DMA_MST_CTRL_EN_A_DISABLE
+ | DMA_MST_CTRL_EN_B_DISABLE
+ | DMA_MST_CTRL_IS_DMA_DONE,
+ &info->reg->dma_mst_ctrl);
+
+ /*
+ * Program and erase have their own busy handlers
+ * status and sequential in needs no delay
+ */
+ switch (command) {
+ case NAND_CMD_READID:
+ writel(NAND_CMD_READID, &info->reg->cmd_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_PIO
+ | CMD_RX |
+ (CMD_TRANS_SIZE_BYTES4 << CMD_TRANS_SIZE_SHIFT)
+ | CMD_CE0,
+ &info->reg->command);
+ info->pio_byte_index = 0;
+ break;
+ case NAND_CMD_READ0:
+ writel(NAND_CMD_READ0, &info->reg->cmd_reg1);
+ writel(NAND_CMD_READSTART, &info->reg->cmd_reg2);
+ writel((page_addr << 16) | (column & 0xFFFF),
+ &info->reg->addr_reg1);
+ writel(page_addr >> 16, &info->reg->addr_reg2);
+ return;
+ case NAND_CMD_SEQIN:
+ writel(NAND_CMD_SEQIN, &info->reg->cmd_reg1);
+ writel(NAND_CMD_PAGEPROG, &info->reg->cmd_reg2);
+ writel((page_addr << 16) | (column & 0xFFFF),
+ &info->reg->addr_reg1);
+ writel(page_addr >> 16,
+ &info->reg->addr_reg2);
+ return;
+ case NAND_CMD_PAGEPROG:
+ return;
+ case NAND_CMD_ERASE1:
+ writel(NAND_CMD_ERASE1, &info->reg->cmd_reg1);
+ writel(NAND_CMD_ERASE2, &info->reg->cmd_reg2);
+ writel(page_addr, &info->reg->addr_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_ALE |
+ CMD_SEC_CMD | CMD_CE0 | CMD_ALE_BYTES3,
+ &info->reg->command);
+ break;
+ case NAND_CMD_RNDOUT:
+ return;
+ case NAND_CMD_ERASE2:
+ return;
+ case NAND_CMD_STATUS:
+ writel(NAND_CMD_STATUS, &info->reg->cmd_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_PIO | CMD_RX
+ | (CMD_TRANS_SIZE_BYTES1 <<
+ CMD_TRANS_SIZE_SHIFT)
+ | CMD_CE0,
+ &info->reg->command);
+ info->pio_byte_index = 0;
+ break;
+ case NAND_CMD_RESET:
+ writel(NAND_CMD_RESET, &info->reg->cmd_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_CE0,
+ &info->reg->command);
+ break;
+ default:
+ return;
+ }
+ if (!nand_waitfor_cmd_completion(info->reg))
+ printf("Command 0x%02X timeout\n", command);
+}
+
+/*
+ * blank_check - check whether the pointed buffer are all FF (blank).
+ * @param: buf - data buffer for blank check
+ * @param: len - length of the buffer in byte
+ * @return:
+ * 1 - blank
+ * 0 - non-blank
+ */
+static int blank_check(u8 *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (buf[i] != 0xFF)
+ return 0;
+ return 1;
+}
+
+/*
+ * check_ecc_error - After a DMA transfer for read, we call this function to
+ * see whether there is any uncorrectable error on the pointed data buffer
+ * or oob buffer.
+ *
+ * @param reg: nand_ctlr structure
+ * @param databuf: data buffer
+ * @param a_len: data buffer length
+ * @param oobbuf: oob buffer
+ * @param b_len: oob buffer length
+ * @return:
+ * ECC_OK - no ECC error or correctable ECC error
+ * ECC_TAG_ERROR - uncorrectable tag ECC error
+ * ECC_DATA_ERROR - uncorrectable data ECC error
+ * ECC_DATA_ERROR + ECC_TAG_ERROR - uncorrectable data+tag ECC error
+ */
+static int check_ecc_error(struct nand_ctlr *reg, u8 *databuf,
+ int a_len, u8 *oobbuf, int b_len)
+{
+ int return_val = ECC_OK;
+ u32 reg_val;
+
+ if (!(readl(&reg->isr) & ISR_IS_ECC_ERR))
+ return ECC_OK;
+
+ reg_val = readl(&reg->dec_status);
+ if ((reg_val & DEC_STATUS_A_ECC_FAIL) && databuf) {
+ reg_val = readl(&reg->bch_dec_status_buf);
+ /*
+ * If uncorrectable error occurs on data area, then see whether
+ * they are all FF. If all are FF, it's a blank page.
+ * Not error.
+ */
+ if ((reg_val & BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK) &&
+ !blank_check(databuf, a_len))
+ return_val |= ECC_DATA_ERROR;
+ }
+
+ if ((reg_val & DEC_STATUS_B_ECC_FAIL) && oobbuf) {
+ reg_val = readl(&reg->bch_dec_status_buf);
+ /*
+ * If uncorrectable error occurs on tag area, then see whether
+ * they are all FF. If all are FF, it's a blank page.
+ * Not error.
+ */
+ if ((reg_val & BCH_DEC_STATUS_FAIL_TAG_MASK) &&
+ !blank_check(oobbuf, b_len))
+ return_val |= ECC_TAG_ERROR;
+ }
+
+ return return_val;
+}
+
+/**
+ * start_command - set GO bit to send command to device
+ * @param reg: nand_ctlr structure
+ */
+static void start_command(struct nand_ctlr *reg)
+{
+ u32 reg_val;
+
+ reg_val = readl(&reg->command);
+ reg_val |= CMD_GO;
+ writel(reg_val, &reg->command);
+}
+
+/**
+ * stop_command - clear command GO bit, DMA GO bit, and DMA completion status
+ * @param reg: nand_ctlr structure
+ */
+static void stop_command(struct nand_ctlr *reg)
+{
+ /* Stop command */
+ writel(0, &reg->command);
+
+ /* Stop DMA engine and clear DMA completion status */
+ writel(DMA_MST_CTRL_GO_DISABLE
+ | DMA_MST_CTRL_IS_DMA_DONE,
+ &reg->dma_mst_ctrl);
+}
+
+/*
+ * set_bus_width_page_size - set up NAND bus width and page size
+ * @param info: nand_info structure
+ * @param *reg_val: address of reg_val
+ * @return: value is set in reg_val
+ */
+static void set_bus_width_page_size(struct fdt_nand *config,
+ u32 *reg_val)
+{
+ if (config->width == 8)
+ *reg_val = CFG_BUS_WIDTH_8BIT;
+ else
+ *reg_val = CFG_BUS_WIDTH_16BIT;
+
+ if (config->page_data_bytes == 256)
+ *reg_val |= CFG_PAGE_SIZE_256;
+ else if (config->page_data_bytes == 512)
+ *reg_val |= CFG_PAGE_SIZE_512;
+ else if (config->page_data_bytes == 1024)
+ *reg_val |= CFG_PAGE_SIZE_1024;
+ else if (config->page_data_bytes == 2048)
+ *reg_val |= CFG_PAGE_SIZE_2048;
+ else if (config->page_data_bytes == 4096)
+ *reg_val |= CFG_PAGE_SIZE_4096;
+}
+
+/**
+ * nand_rw_page - page read/write function
+ * @param mtd: mtd info structure
+ * @param chip: nand chip info structure
+ * @param buf: data buffer
+ * @param page: page number
+ * @param with_ecc: 1 to enable ECC, 0 to disable ECC
+ * @param is_writing: 0 for read, 1 for write
+ * @return: 0 when successfully completed
+ * -EIO when command timeout
+ */
+static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int page, int with_ecc, int is_writing)
+{
+ u32 reg_val;
+ int tag_size;
+ struct nand_oobfree *free = chip->ecc.layout->oobfree;
+ /* 128 is larger than the value that our HW can support. */
+ u32 tag_buf[128];
+ char *tag_ptr;
+ struct nand_info *info;
+ struct fdt_nand *config;
+
+ if (((int) buf) & 0x03) {
+ printf("buf 0x%X has to be 4-byte aligned\n", (u32) buf);
+ return -EINVAL;
+ }
+
+ info = (struct nand_info *) chip->priv;
+ config = &info->config;
+
+ /* Need to be 4-byte aligned */
+ tag_ptr = (char *) &tag_buf;
+
+ stop_command(info->reg);
+
+ writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a);
+ writel((u32) buf, &info->reg->data_block_ptr);
+
+ if (with_ecc) {
+ writel((u32) tag_ptr, &info->reg->tag_ptr);
+ if (is_writing)
+ memcpy(tag_ptr, chip->oob_poi + free->offset,
+ config->tag_bytes +
+ config->tag_ecc_bytes);
+ } else
+ writel((u32) chip->oob_poi, &info->reg->tag_ptr);
+
+ set_bus_width_page_size(&info->config, &reg_val);
+
+ /* Set ECC selection, configure ECC settings */
+ if (with_ecc) {
+ tag_size = config->tag_bytes + config->tag_ecc_bytes;
+ reg_val |= (CFG_SKIP_SPARE_SEL_4
+ | CFG_SKIP_SPARE_ENABLE
+ | CFG_HW_ECC_CORRECTION_ENABLE
+ | CFG_ECC_EN_TAG_DISABLE
+ | CFG_HW_ECC_SEL_RS
+ | CFG_HW_ECC_ENABLE
+ | CFG_TVAL4
+ | (tag_size - 1));
+
+ if (!is_writing) {
+ tag_size += config->skipped_spare_bytes;
+ invalidate_dcache_range((unsigned long) tag_ptr,
+ ((unsigned long) tag_ptr) + tag_size);
+ } else
+ flush_dcache_range((unsigned long) tag_ptr,
+ ((unsigned long) tag_ptr) + tag_size);
+ } else {
+ tag_size = mtd->oobsize;
+ reg_val |= (CFG_SKIP_SPARE_DISABLE
+ | CFG_HW_ECC_CORRECTION_DISABLE
+ | CFG_ECC_EN_TAG_DISABLE
+ | CFG_HW_ECC_DISABLE
+ | (tag_size - 1));
+ if (!is_writing) {
+ invalidate_dcache_range((unsigned long) chip->oob_poi,
+ ((unsigned long) chip->oob_poi) + tag_size);
+ } else {
+ flush_dcache_range((unsigned long) chip->oob_poi,
+ ((unsigned long) chip->oob_poi) + tag_size);
+ }
+ }
+ writel(reg_val, &info->reg->config);
+
+ if (!is_writing) {
+ invalidate_dcache_range((unsigned long) buf,
+ ((unsigned long) buf) +
+ (1 << chip->page_shift));
+ } else {
+ flush_dcache_range((unsigned long) buf,
+ ((unsigned long) buf) +
+ (1 << chip->page_shift));
+ }
+
+ writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
+
+ writel(tag_size - 1, &info->reg->dma_cfg_b);
+
+ nand_clear_interrupt_status(info->reg);
+
+ reg_val = CMD_CLE | CMD_ALE
+ | CMD_SEC_CMD
+ | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT)
+ | CMD_A_VALID
+ | CMD_B_VALID
+ | (CMD_TRANS_SIZE_BYTES_PAGE_SIZE_SEL <<
+ CMD_TRANS_SIZE_SHIFT)
+ | CMD_CE0;
+ if (!is_writing)
+ reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX);
+ else
+ reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX);
+ writel(reg_val, &info->reg->command);
+
+ /* Setup DMA engine */
+ reg_val = DMA_MST_CTRL_GO_ENABLE
+ | DMA_MST_CTRL_BURST_8WORDS
+ | DMA_MST_CTRL_EN_A_ENABLE
+ | DMA_MST_CTRL_EN_B_ENABLE;
+
+ if (!is_writing)
+ reg_val |= DMA_MST_CTRL_DIR_READ;
+ else
+ reg_val |= DMA_MST_CTRL_DIR_WRITE;
+
+ writel(reg_val, &info->reg->dma_mst_ctrl);
+
+ start_command(info->reg);
+
+ if (!nand_waitfor_cmd_completion(info->reg)) {
+ if (!is_writing)
+ printf("Read Page 0x%X timeout ", page);
+ else
+ printf("Write Page 0x%X timeout ", page);
+ if (with_ecc)
+ printf("with ECC");
+ else
+ printf("without ECC");
+ printf("\n");
+ return -EIO;
+ }
+
+ if (with_ecc && !is_writing) {
+ memcpy(chip->oob_poi, tag_ptr,
+ config->skipped_spare_bytes);
+ memcpy(chip->oob_poi + free->offset,
+ tag_ptr + config->skipped_spare_bytes,
+ config->tag_bytes);
+ reg_val = (u32) check_ecc_error(info->reg, (u8 *) buf,
+ 1 << chip->page_shift,
+ (u8 *) (tag_ptr + config->skipped_spare_bytes),
+ config->tag_bytes);
+ if (reg_val & ECC_TAG_ERROR)
+ printf("Read Page 0x%X tag ECC error\n", page);
+ if (reg_val & ECC_DATA_ERROR)
+ printf("Read Page 0x%X data ECC error\n",
+ page);
+ if (reg_val & (ECC_DATA_ERROR | ECC_TAG_ERROR))
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * nand_read_page_hwecc - hardware ecc based page read function
+ * @param mtd: mtd info structure
+ * @param chip: nand chip info structure
+ * @param buf: buffer to store read data
+ * @param page: page number to read
+ * @return: 0 when successfully completed
+ * -EIO when command timeout
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int page)
+{
+ return nand_rw_page(mtd, chip, buf, page, 1, 0);
+}
+
+/**
+ * nand_write_page_hwecc - hardware ecc based page write function
+ * @param mtd: mtd info structure
+ * @param chip: nand chip info structure
+ * @param buf: data buffer
+ */
+static void nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf)
+{
+ int page;
+ struct nand_info *info;
+
+ info = (struct nand_info *) chip->priv;
+
+ page = (readl(&info->reg->addr_reg1) >> 16) |
+ (readl(&info->reg->addr_reg2) << 16);
+
+ nand_rw_page(mtd, chip, (uint8_t *) buf, page, 1, 1);
+}
+
+
+/**
+ * nand_read_page_raw - read raw page data without ecc
+ * @param mtd: mtd info structure
+ * @param chip: nand chip info structure
+ * @param buf: buffer to store read data
+ * @param page: page number to read
+ * @return: 0 when successfully completed
+ * -EINVAL when chip->oob_poi is not double-word aligned
+ * -EIO when command timeout
+ */
+static int nand_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int page)
+{
+ return nand_rw_page(mtd, chip, buf, page, 0, 0);
+}
+
+/**
+ * nand_write_page_raw - raw page write function
+ * @param mtd: mtd info structure
+ * @param chip: nand chip info structure
+ * @param buf: data buffer
+ */
+static void nand_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf)
+{
+ int page;
+ struct nand_info *info;
+
+ info = (struct nand_info *) chip->priv;
+ page = (readl(&info->reg->addr_reg1) >> 16) |
+ (readl(&info->reg->addr_reg2) << 16);
+
+ nand_rw_page(mtd, chip, (uint8_t *) buf, page, 0, 1);
+}
+
+/**
+ * nand_rw_oob - OOB data read/write function
+ * @param mtd: mtd info structure
+ * @param chip: nand chip info structure
+ * @param page: page number to read
+ * @param with_ecc: 1 to enable ECC, 0 to disable ECC
+ * @param is_writing: 0 for read, 1 for write
+ * @return: 0 when successfully completed
+ * -EINVAL when chip->oob_poi is not double-word aligned
+ * -EIO when command timeout
+ */
+static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int with_ecc, int is_writing)
+{
+ u32 reg_val;
+ int tag_size;
+ struct nand_oobfree *free = chip->ecc.layout->oobfree;
+ struct nand_info *info;
+
+ if (((int) chip->oob_poi) & 0x03)
+ return -EINVAL;
+
+ info = (struct nand_info *) chip->priv;
+ stop_command(info->reg);
+
+ writel((u32) chip->oob_poi, &info->reg->tag_ptr);
+
+ set_bus_width_page_size(&info->config, &reg_val);
+
+ /* Set ECC selection */
+ tag_size = mtd->oobsize;
+ if (with_ecc)
+ reg_val |= CFG_ECC_EN_TAG_ENABLE;
+ else
+ reg_val |= (CFG_ECC_EN_TAG_DISABLE);
+
+ reg_val |= ((tag_size - 1) |
+ CFG_SKIP_SPARE_DISABLE |
+ CFG_HW_ECC_CORRECTION_DISABLE |
+ CFG_HW_ECC_DISABLE);
+ writel(reg_val, &info->reg->config);
+
+ if (!is_writing)
+ invalidate_dcache_range((unsigned long) chip->oob_poi,
+ ((unsigned long) chip->oob_poi) + tag_size);
+ else
+ flush_dcache_range((unsigned long) chip->oob_poi,
+ ((unsigned long) chip->oob_poi) + tag_size);
+
+ writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
+
+ if (is_writing && with_ecc)
+ tag_size -= info->config.tag_ecc_bytes;
+
+ writel(tag_size - 1, &info->reg->dma_cfg_b);
+
+ nand_clear_interrupt_status(info->reg);
+
+ reg_val = CMD_CLE | CMD_ALE
+ | CMD_SEC_CMD
+ | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT)
+ | CMD_B_VALID
+ | CMD_CE0;
+ if (!is_writing)
+ reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX);
+ else
+ reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX);
+ writel(reg_val, &info->reg->command);
+
+ /* Setup DMA engine */
+ reg_val = DMA_MST_CTRL_GO_ENABLE
+ | DMA_MST_CTRL_BURST_8WORDS
+ | DMA_MST_CTRL_EN_B_ENABLE;
+ if (!is_writing)
+ reg_val |= DMA_MST_CTRL_DIR_READ;
+ else
+ reg_val |= DMA_MST_CTRL_DIR_WRITE;
+
+ writel(reg_val, &info->reg->dma_mst_ctrl);
+
+ start_command(info->reg);
+
+ if (!nand_waitfor_cmd_completion(info->reg)) {
+ if (!is_writing)
+ printf("Read OOB of Page 0x%X timeout\n", page);
+ else
+ printf("Write OOB of Page 0x%X timeout\n", page);
+ return -EIO;
+ }
+
+ if (with_ecc && !is_writing) {
+ reg_val = (u32) check_ecc_error(info->reg, 0, 0,
+ (u8 *) (chip->oob_poi + free->offset),
+ info->config.tag_bytes);
+ if (reg_val & ECC_TAG_ERROR)
+ printf("Read OOB of Page 0x%X tag ECC error\n", page);
+ }
+ return 0;
+}
+
+/**
+ * nand_read_oob - OOB data read function
+ * @param mtd: mtd info structure
+ * @param chip: nand chip info structure
+ * @param page: page number to read
+ * @param sndcmd: flag whether to issue read command or not
+ * @return: 1 - issue read command next time
+ * 0 - not to issue
+ */
+static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int sndcmd)
+{
+ if (sndcmd) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ sndcmd = 0;
+ }
+ nand_rw_oob(mtd, chip, page, 0, 0);
+ return sndcmd;
+}
+
+/**
+ * nand_write_oob - OOB data write function
+ * @param mtd: mtd info structure
+ * @param chip: nand chip info structure
+ * @param page: page number to write
+ * @return: 0 when successfully completed
+ * -EINVAL when chip->oob_poi is not double-word aligned
+ * -EIO when command timeout
+ */
+static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+
+ return nand_rw_oob(mtd, chip, page, 0, 1);
+}
+
+static void setup_timing(int timing[FDT_NAND_TIMING_COUNT],
+ struct nand_ctlr *reg)
+{
+ u32 reg_val, clk_rate, clk_period, time_val;
+
+ clk_rate = (u32) clock_get_periph_rate(PERIPH_ID_NDFLASH,
+ CLOCK_ID_PERIPH) / 1000000;
+ clk_period = 1000 / clk_rate;
+ reg_val = ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) <<
+ TIMING_TRP_RESP_CNT_SHIFT) & TIMING_TRP_RESP_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWB] / clk_period) <<
+ TIMING_TWB_CNT_SHIFT) & TIMING_TWB_CNT_MASK;
+ time_val = timing[FDT_NAND_MAX_TCR_TAR_TRR] / clk_period;
+ if (time_val > 2)
+ reg_val |= ((time_val - 2) << TIMING_TCR_TAR_TRR_CNT_SHIFT) &
+ TIMING_TCR_TAR_TRR_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWHR] / clk_period) <<
+ TIMING_TWHR_CNT_SHIFT) & TIMING_TWHR_CNT_MASK;
+ time_val = timing[FDT_NAND_MAX_TCS_TCH_TALS_TALH] / clk_period;
+ if (time_val > 1)
+ reg_val |= ((time_val - 1) << TIMING_TCS_CNT_SHIFT) &
+ TIMING_TCS_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWH] / clk_period) <<
+ TIMING_TWH_CNT_SHIFT) & TIMING_TWH_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWP] / clk_period) <<
+ TIMING_TWP_CNT_SHIFT) & TIMING_TWP_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TRH] / clk_period) <<
+ TIMING_TRH_CNT_SHIFT) & TIMING_TRH_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) <<
+ TIMING_TRP_CNT_SHIFT) & TIMING_TRP_CNT_MASK;
+ writel(reg_val, &reg->timing);
+
+ reg_val = 0;
+ time_val = timing[FDT_NAND_TADL] / clk_period;
+ if (time_val > 2)
+ reg_val = (time_val - 2) & TIMING2_TADL_CNT_MASK;
+ writel(reg_val, &reg->timing2);
+}
+
+/*
+ * Board-specific NAND initialization.
+ * @param nand: nand chip info structure
+ * @return: 0, after initialized.
+ */
+int board_nand_init(struct nand_chip *nand)
+{
+ struct nand_info *info = &nand_ctrl;
+ struct fdt_nand *config = &info->config;
+ struct mtd_info tmp_mtd;
+ int tmp_manf, tmp_id, tmp_4th;
+ char compat[8];
+ int node;
+
+#ifndef CONFIG_COLIBRI_T30
+ /* Adjust controller clock rate */
+ clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, CLK_52M);
+
+ /* Pinmux KBCx_SEL uses NAND */
+//move to board configuration
+ pinmux_set_func(PINGRP_KBCA, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_KBCB, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_KBCC, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_KBCD, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_KBCE, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_KBCF, PMUX_FUNC_NAND);
+#else
+ /* Adjust controller clock rate */
+ clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, CLK_52M);
+
+ /* Pinmux NAND on GMI*/
+ //TODO move to board configuration
+ pinmux_set_func(PINGRP_GMI_AD0, PMUX_FUNC_NAND); /* nand D[0:7] */
+ pinmux_set_func(PINGRP_GMI_AD1, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_GMI_AD2, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_GMI_AD3, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_GMI_AD4, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_GMI_AD5, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_GMI_AD6, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_GMI_AD7, PMUX_FUNC_NAND);
+ pinmux_set_func(PINGRP_GMI_ADV_N, PMUX_FUNC_NAND); /* nand ALE */
+ pinmux_set_func(PINGRP_GMI_CLK, PMUX_FUNC_NAND); /* nand CLE */
+ pinmux_set_func(PINGRP_GMI_CS2_N, PMUX_FUNC_NAND); /* nand CE0_N */
+ pinmux_set_func(PINGRP_GMI_CS3_N, PMUX_FUNC_NAND); /* nand CE1_N */
+#if 0 /* on T30 only CE0 is used, the following are reserved */
+ pinmux_set_func(PINGRP_GMI_CS4_N, PMUX_FUNC_NAND); /* nand CE2_N */
+ pinmux_set_func(PINGRP_GMI_IORDY, PMUX_FUNC_NAND); /* nand CE3_N */
+#endif
+ pinmux_set_func(PINGRP_GMI_DQS, PMUX_FUNC_NAND); /* nand DQS, used only for sync nands */
+ pinmux_set_func(PINGRP_GMI_OE_N, PMUX_FUNC_NAND); /* nand RE */
+ pinmux_set_func(PINGRP_GMI_WAIT, PMUX_FUNC_NAND); /* nand BSY */
+ pinmux_set_func(PINGRP_GMI_WP_N, PMUX_FUNC_GMI); /* nand WP, note that this special function is shared with GMI_WP, probably, at least that is what I read in DG-05576-001_v08_InterfaceDesignGuide*/
+ pinmux_set_func(PINGRP_GMI_WR_N, PMUX_FUNC_NAND); /* nand WE */
+#endif
+ nand->priv = &nand_ctrl;
+
+ /* Setup fake MTD structure */
+ tmp_mtd.priv = nand;
+ tmp_mtd.writesize = 0;
+ info->reg = (void *)0x70008000;
+
+#ifdef CONFIG_COLIBRI_T30
+ /* MAX Set compatibility to Tegra 2 */
+ printf("InitTiming ");
+ printf("NAND Config2 was %X ", readl((unsigned *)0x70008000 + 0xd8) );
+
+ writel(0, (unsigned *)0x70008000 + 0xd8);
+ printf("changed to %X \n", readl((unsigned *)0x70008000 + 0xd8) );
+#endif
+
+#if 0
+ printf(" CLK_RST_CONTROLLER_RST_DEVICES_L_0 0x%8x \n", readl( (unsigned*)0x60006004 ) & (1<<13));
+ printf(" CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0 0x%8x \n", readl( (unsigned*)0x60006010 ) & (1<<13));
+ printf(" CLK_RST_CONTROLLER_CLK_SOURCE_NDFLASH_0 0x%8x \n", readl( (unsigned*)0x60006160 ));
+ printf("status 0x%8x \n", readl(&(info->reg->status)) );
+ printf("isr 0x%8x \n", readl(&(info->reg->isr)) );
+ printf("ier 0x%8x \n", readl(&(info->reg->ier)) );
+ printf("config 0x%8x \n", readl(&(info->reg->config)) );
+ printf("timing 0x%8x \n", readl(&(info->reg->timing)) );
+ printf("resp 0x%8x \n", readl(&(info->reg->resp)) );
+ printf("timing2 0x%8x \n", readl(&(info->reg->timing2)) );
+ printf("command 0x%8x \n", readl(&(info->reg->command)) );
+ printf("status 0x%8x \n", readl(&(info->reg->status)) );
+ printf("isr 0x%8x \n", readl(&(info->reg->isr)) );
+ printf("ier 0x%8x \n", readl(&(info->reg->ier)) );
+ printf("config 0x%8x \n", readl(&(info->reg->config)) );
+ printf("timing 0x%8x \n", readl(&(info->reg->timing)) );
+ printf("resp 0x%8x \n", readl(&(info->reg->resp)) );
+ printf("timing2 0x%8x \n", readl(&(info->reg->timing2)) );
+ printf("cmd_reg1 0x%8x \n", readl(&(info->reg->cmd_reg1)) );
+ printf("cmd_reg2 0x%8x \n", readl(&(info->reg->cmd_reg2)) );
+ printf("addr_reg1 0x%8x \n", readl(&(info->reg->addr_reg1)) );
+ printf("addr_reg2 0x%8x \n", readl(&(info->reg->addr_reg2)) );
+ printf("dma_mst_ctrl 0x%8x \n", readl(&(info->reg->dma_mst_ctrl)) );
+ printf("dma_cfg_a 0x%8x \n", readl(&(info->reg->dma_cfg_a)) );
+ printf("dma_cfg_b 0x%8x \n", readl(&(info->reg->dma_cfg_b)) );
+ printf("fifo_ctrl 0x%8x \n", readl(&(info->reg->fifo_ctrl)) );
+ printf("data_block_ptr 0x%8x \n", readl(&(info->reg->data_block_ptr)) );
+ printf("tag_ptr 0x%8x \n", readl(&(info->reg->tag_ptr)) );
+ printf("dec_status 0x%8x \n", readl(&(info->reg->dec_status)) );
+ printf("hwstatus_cmd 0x%8x \n", readl(&(info->reg->hwstatus_cmd)) );
+ printf("hwstatus_mask 0x%8x \n", readl(&(info->reg->hwstatus_mask)) );
+ printf("bch_config 0x%8x \n", readl(&(info->reg->bch_config)) );
+ printf("bch_dec_result 0x%8x \n", readl(&(info->reg->bch_dec_result)) );
+ printf("bch_dec_status_buf 0x%8x \n", readl(&(info->reg->bch_dec_status_buf)) );
+#endif
+ /* Set initial scan timing */
+ writel(SCAN_TIMING_VAL, &(info->reg->timing));
+ writel(SCAN_TIMING2_VAL, &(info->reg->timing2));
+ /* reset the nand, as the bootrom does this only when nand is the bootdevice */
+ nand_command(&tmp_mtd, NAND_CMD_RESET, -1, -1);
+
+#ifdef CONFIG_COLIBRI_T30
+ printf("ReadID ");
+#endif
+ /* Send command for reading device ID */
+ nand_command(&tmp_mtd, NAND_CMD_READID, 0x00, -1);
+
+ /* Read manufacturer and device IDs */
+ tmp_manf = nand_read_byte(&tmp_mtd);
+ tmp_id = nand_read_byte(&tmp_mtd);
+ tmp_4th = nand_read_byte(&tmp_mtd); /* the 3rd byte is not needed, skip over it */
+ tmp_4th = nand_read_byte(&tmp_mtd);
+
+#ifdef CONFIG_COLIBRI_T30
+ printf("NAND ReadID Man %02X, ID %02X, 3th %02X, 4th %02X", tmp_manf, tmp_id, tmp_3rd, tmp_4th);
+#endif
+
+ sprintf(compat, "%02X,%02X,%02X", tmp_manf, tmp_id, tmp_4th);
+ node = fdt_node_offset_by_compatible(gd->blob, 0, compat);
+ if (node < 0) {
+//fall back to former nand-flash node
+ printf("Could not find NAND flash device node\n");
+ return -1;
+ }
+ if (fdt_decode_nand(gd->blob, node, config)) {
+ printf("Could not decode NAND flash device node\n");
+ return -1;
+ }
+
+ //copy partition information to global data
+ gd->env_offset = (unsigned)(config->nv_partitions[FDT_NAND_ENV_OFFSET]);
+ gd->conf_blk_offset = (unsigned)(config->nv_partitions[FDT_NAND_CONFIG_OFFSET]);
+ gd->conf_blk_offset2 = (unsigned)(config->nv_partitions[FDT_NAND_CONFIG_OFFSET2]);
+ gd->kernel_offset = (unsigned)(config->nv_partitions[FDT_NAND_KERNEL_OFFSET]);
+ gd->rootfs_size = (unsigned)(config->nv_partitions[FDT_NAND_ROOTFS_LENGTH]);
+ gd->rootfs_offset = (unsigned)(config->nv_partitions[FDT_NAND_ROOTFS_SIZE]);
+
+ if (!config->enabled)
+ return -1;
+ info->reg = config->reg;
+
+ eccoob.eccbytes = config->data_ecc_bytes + config->tag_ecc_bytes;
+ eccoob.oobavail = config->tag_bytes;
+ eccoob.oobfree[0].offset = config->skipped_spare_bytes +
+ config->data_ecc_bytes;
+ eccoob.oobfree[0].length = config->tag_bytes;
+
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.layout = &eccoob;
+ nand->ecc.size = config->page_data_bytes;
+ nand->ecc.bytes = config->page_spare_bytes;
+
+ nand->options = LP_OPTIONS;
+ nand->cmdfunc = nand_command;
+ nand->read_byte = nand_read_byte;
+ nand->read_buf = nand_read_buf;
+ nand->write_buf = nand_write_buf;
+ nand->ecc.read_page = nand_read_page_hwecc;
+ nand->ecc.write_page = nand_write_page_hwecc;
+ nand->ecc.read_page_raw = nand_read_page_raw;
+ nand->ecc.write_page_raw = nand_write_page_raw;
+ nand->ecc.read_oob = nand_read_oob;
+ nand->ecc.write_oob = nand_write_oob;
+ nand->cmd_ctrl = nand_hwcontrol;
+ nand->dev_ready = nand_dev_ready;
+
+ /* Adjust timing for NAND device */
+ setup_timing(config->timing, info->reg);
+
+ fdt_setup_gpio(&config->wp_gpio);
+
+ return 0;
+}
diff --git a/board/toradex/common/tegra2_nand.h b/board/toradex/common/tegra2_nand.h
new file mode 100644
index 0000000000..9ecb80c50f
--- /dev/null
+++ b/board/toradex/common/tegra2_nand.h
@@ -0,0 +1,280 @@
+enum {
+ Bit0 = 1 << 0,
+ Bit1 = 1 << 1,
+ Bit2 = 1 << 2,
+ Bit3 = 1 << 3,
+ Bit4 = 1 << 4,
+ Bit5 = 1 << 5,
+ Bit6 = 1 << 6,
+ Bit7 = 1 << 7,
+ Bit8 = 1 << 8,
+ Bit9 = 1 << 9,
+ Bit10 = 1 << 10,
+ Bit11 = 1 << 11,
+ Bit12 = 1 << 12,
+ Bit13 = 1 << 13,
+ Bit14 = 1 << 14,
+ Bit15 = 1 << 15,
+ Bit16 = 1 << 16,
+ Bit17 = 1 << 17,
+ Bit18 = 1 << 18,
+ Bit19 = 1 << 19,
+ Bit20 = 1 << 20,
+ Bit21 = 1 << 21,
+ Bit22 = 1 << 22,
+ Bit23 = 1 << 23,
+ Bit24 = 1 << 24,
+ Bit25 = 1 << 25,
+ Bit26 = 1 << 26,
+ Bit27 = 1 << 27,
+ Bit28 = 1 << 28,
+ Bit29 = 1 << 29,
+ Bit30 = 1 << 30,
+ Bit31 = 1 << 31
+};
+
+/* register offset */
+#define COMMAND_0 0x00
+#define CMD_GO Bit31
+#define CMD_CLE Bit30
+#define CMD_ALE Bit29
+#define CMD_PIO Bit28
+#define CMD_TX Bit27
+#define CMD_RX Bit26
+#define CMD_SEC_CMD Bit25
+#define CMD_AFT_DAT_MASK Bit24
+#define CMD_AFT_DAT_DISABLE 0
+#define CMD_AFT_DAT_ENABLE Bit24
+#define CMD_TRANS_SIZE_SHIFT 20
+enum {
+ CMD_TRANS_SIZE_BYTES1 = 0,
+ CMD_TRANS_SIZE_BYTES2,
+ CMD_TRANS_SIZE_BYTES3,
+ CMD_TRANS_SIZE_BYTES4,
+ CMD_TRANS_SIZE_BYTES5,
+ CMD_TRANS_SIZE_BYTES6,
+ CMD_TRANS_SIZE_BYTES7,
+ CMD_TRANS_SIZE_BYTES8,
+ CMD_TRANS_SIZE_BYTES_PAGE_SIZE_SEL
+};
+
+#define CMD_TRANS_SIZE_BYTES_PAGE_SIZE_SEL 8
+#define CMD_A_VALID Bit19
+#define CMD_B_VALID Bit18
+#define CMD_RD_STATUS_CHK Bit17
+#define CMD_R_BSY_CHK Bit16
+#define CMD_CE7 Bit15
+#define CMD_CE6 Bit14
+#define CMD_CE5 Bit13
+#define CMD_CE4 Bit12
+#define CMD_CE3 Bit11
+#define CMD_CE2 Bit10
+#define CMD_CE1 Bit9
+#define CMD_CE0 Bit8
+#define CMD_CLE_BYTE_SIZE_SHIFT 4
+enum {
+ CMD_CLE_BYTES1 = 0,
+ CMD_CLE_BYTES2,
+ CMD_CLE_BYTES3,
+ CMD_CLE_BYTES4,
+};
+#define CMD_ALE_BYTE_SIZE_SHIFT 0
+enum {
+ CMD_ALE_BYTES1 = 0,
+ CMD_ALE_BYTES2,
+ CMD_ALE_BYTES3,
+ CMD_ALE_BYTES4,
+ CMD_ALE_BYTES5,
+ CMD_ALE_BYTES6,
+ CMD_ALE_BYTES7,
+ CMD_ALE_BYTES8
+};
+
+#define STATUS_0 0x04
+#define STATUS_RBSY0 Bit8
+
+#define ISR_0 0x08
+#define ISR_IS_CMD_DONE Bit5
+#define ISR_IS_ECC_ERR Bit4
+
+#define IER_0 0x0C
+
+#define CFG_0 0x10
+#define CFG_HW_ECC_MASK Bit31
+#define CFG_HW_ECC_DISABLE 0
+#define CFG_HW_ECC_ENABLE Bit31
+#define CFG_HW_ECC_SEL_MASK Bit30
+#define CFG_HW_ECC_SEL_HAMMING 0
+#define CFG_HW_ECC_SEL_RS Bit30
+#define CFG_HW_ECC_CORRECTION_MASK Bit29
+#define CFG_HW_ECC_CORRECTION_DISABLE 0
+#define CFG_HW_ECC_CORRECTION_ENABLE Bit29
+#define CFG_PIPELINE_EN_MASK Bit28
+#define CFG_PIPELINE_EN_DISABLE 0
+#define CFG_PIPELINE_EN_ENABLE Bit28
+#define CFG_ECC_EN_TAG_MASK Bit27
+#define CFG_ECC_EN_TAG_DISABLE 0
+#define CFG_ECC_EN_TAG_ENABLE Bit27
+#define CFG_TVALUE_MASK (Bit25 | Bit24)
+enum {
+ CFG_TVAL4 = 0 << 24,
+ CFG_TVAL6 = 1 << 24,
+ CFG_TVAL8 = 2 << 24
+};
+#define CFG_SKIP_SPARE_MASK Bit23
+#define CFG_SKIP_SPARE_DISABLE 0
+#define CFG_SKIP_SPARE_ENABLE Bit23
+#define CFG_COM_BSY_MASK Bit22
+#define CFG_COM_BSY_DISABLE 0
+#define CFG_COM_BSY_ENABLE Bit22
+#define CFG_BUS_WIDTH_MASK Bit21
+#define CFG_BUS_WIDTH_8BIT 0
+#define CFG_BUS_WIDTH_16BIT Bit21
+#define CFG_LPDDR1_MODE_MASK Bit20
+#define CFG_LPDDR1_MODE_DISABLE 0
+#define CFG_LPDDR1_MODE_ENABLE Bit20
+#define CFG_EDO_MODE_MASK Bit19
+#define CFG_EDO_MODE_DISABLE 0
+#define CFG_EDO_MODE_ENABLE Bit19
+#define CFG_PAGE_SIZE_SEL_MASK (Bit18 | Bit17 | Bit16)
+enum {
+ CFG_PAGE_SIZE_256 = 0 << 16,
+ CFG_PAGE_SIZE_512 = 1 << 16,
+ CFG_PAGE_SIZE_1024 = 2 << 16,
+ CFG_PAGE_SIZE_2048 = 3 << 16,
+ CFG_PAGE_SIZE_4096 = 4 << 16
+};
+#define CFG_SKIP_SPARE_SEL_MASK (Bit15 | Bit14)
+enum {
+ CFG_SKIP_SPARE_SEL_4 = 0 << 14,
+ CFG_SKIP_SPARE_SEL_8 = 1 << 14,
+ CFG_SKIP_SPARE_SEL_12 = 2 << 14,
+ CFG_SKIP_SPARE_SEL_16 = 3 << 14
+};
+#define CFG_TAG_BYTE_SIZE_MASK 0x1FF
+
+#define TIMING_0 0x14
+#define TIMING_TRP_RESP_CNT_SHIFT 28
+#define TIMING_TRP_RESP_CNT_MASK (Bit31 | Bit30 | Bit29 | Bit28)
+#define TIMING_TWB_CNT_SHIFT 24
+#define TIMING_TWB_CNT_MASK (Bit27 | Bit26 | Bit25 | Bit24)
+#define TIMING_TCR_TAR_TRR_CNT_SHIFT 20
+#define TIMING_TCR_TAR_TRR_CNT_MASK (Bit23 | Bit22 | Bit21 | Bit20)
+#define TIMING_TWHR_CNT_SHIFT 16
+#define TIMING_TWHR_CNT_MASK (Bit19 | Bit18 | Bit17 | Bit16)
+#define TIMING_TCS_CNT_SHIFT 14
+#define TIMING_TCS_CNT_MASK (Bit15 | Bit14)
+#define TIMING_TWH_CNT_SHIFT 12
+#define TIMING_TWH_CNT_MASK (Bit13 | Bit12)
+#define TIMING_TWP_CNT_SHIFT 8
+#define TIMING_TWP_CNT_MASK (Bit11 | Bit10 | Bit9 | Bit8)
+#define TIMING_TRH_CNT_SHIFT 4
+#define TIMING_TRH_CNT_MASK (Bit5 | Bit4)
+#define TIMING_TRP_CNT_SHIFT 0
+#define TIMING_TRP_CNT_MASK (Bit3 | Bit2 | Bit1 | Bit0)
+
+#define RESP_0 0x18
+
+#define TIMING2_0 0x1C
+#define TIMING2_TADL_CNT_SHIFT 0
+#define TIMING2_TADL_CNT_MASK (Bit3 | Bit2 | Bit1 | Bit0)
+
+#define CMD_REG1_0 0x20
+#define CMD_REG2_0 0x24
+#define ADDR_REG1_0 0x28
+#define ADDR_REG2_0 0x2C
+
+#define DMA_MST_CTRL_0 0x30
+#define DMA_MST_CTRL_GO_MASK Bit31
+#define DMA_MST_CTRL_GO_DISABLE 0
+#define DMA_MST_CTRL_GO_ENABLE Bit31
+#define DMA_MST_CTRL_DIR_MASK Bit30
+#define DMA_MST_CTRL_DIR_READ 0
+#define DMA_MST_CTRL_DIR_WRITE Bit30
+#define DMA_MST_CTRL_PERF_EN_MASK Bit29
+#define DMA_MST_CTRL_PERF_EN_DISABLE 0
+#define DMA_MST_CTRL_PERF_EN_ENABLE Bit29
+#define DMA_MST_CTRL_REUSE_BUFFER_MASK Bit27
+#define DMA_MST_CTRL_REUSE_BUFFER_DISABLE 0
+#define DMA_MST_CTRL_REUSE_BUFFER_ENABLE Bit27
+#define DMA_MST_CTRL_BURST_SIZE_MASK (Bit26 | Bit25 | Bit24)
+enum {
+ DMA_MST_CTRL_BURST_1WORDS = 2 << 24,
+ DMA_MST_CTRL_BURST_4WORDS = 3 << 24,
+ DMA_MST_CTRL_BURST_8WORDS = 4 << 24,
+ DMA_MST_CTRL_BURST_16WORDS = 5 << 24
+};
+#define DMA_MST_CTRL_IS_DMA_DONE Bit20
+#define DMA_MST_CTRL_EN_A_MASK Bit2
+#define DMA_MST_CTRL_EN_A_DISABLE 0
+#define DMA_MST_CTRL_EN_A_ENABLE Bit2
+#define DMA_MST_CTRL_EN_B_MASK Bit1
+#define DMA_MST_CTRL_EN_B_DISABLE 0
+#define DMA_MST_CTRL_EN_B_ENABLE Bit1
+
+#define DMA_CFG_A_0 0x34
+#define DMA_CFG_B_0 0x38
+#define FIFO_CTRL_0 0x3C
+#define DATA_BLOCK_PTR_0 0x40
+#define TAG_PTR_0 0x44
+#define ECC_PTR_0 0x48
+
+#define DEC_STATUS_0 0x4C
+#define DEC_STATUS_A_ECC_FAIL Bit1
+#define DEC_STATUS_B_ECC_FAIL Bit0
+
+#define BCH_CONFIG_0 0xCC
+#define BCH_CONFIG_BCH_TVALUE_MASK (Bit5 | Bit4)
+enum {
+ BCH_CONFIG_BCH_TVAL4 = 0 << 4,
+ BCH_CONFIG_BCH_TVAL8 = 1 << 4,
+ BCH_CONFIG_BCH_TVAL14 = 2 << 4,
+ BCH_CONFIG_BCH_TVAL16 = 3 << 4
+};
+#define BCH_CONFIG_BCH_ECC_MASK Bit0
+#define BCH_CONFIG_BCH_ECC_DISABLE 0
+#define BCH_CONFIG_BCH_ECC_ENABLE Bit0
+
+#define BCH_DEC_RESULT_0 0xD0
+#define BCH_DEC_RESULT_CORRFAIL_ERR_MASK Bit8
+#define BCH_DEC_RESULT_PAGE_COUNT_MASK 0xFF
+
+#define BCH_DEC_STATUS_BUF_0 0xD4
+#define BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK 0xFF000000
+#define BCH_DEC_STATUS_CORR_SEC_FLAG_MASK 0x00FF0000
+#define BCH_DEC_STATUS_FAIL_TAG_MASK Bit14
+#define BCH_DEC_STATUS_CORR_TAG_MASK Bit13
+#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK (Bit12 | Bit11 | Bit10 | Bit9 | Bit8)
+#define BCH_DEC_STATUS_PAGE_NUMBER_MASK 0xFF
+
+#define LP_OPTIONS (NAND_NO_READRDY | NAND_NO_AUTOINCR)
+
+struct nand_ctlr {
+ u32 command; /* offset 00h */
+ u32 status; /* offset 04h */
+ u32 isr; /* offset 08h */
+ u32 ier; /* offset 0Ch */
+ u32 config; /* offset 10h */
+ u32 timing; /* offset 14h */
+ u32 resp; /* offset 18h */
+ u32 timing2; /* offset 1Ch */
+ u32 cmd_reg1; /* offset 20h */
+ u32 cmd_reg2; /* offset 24h */
+ u32 addr_reg1; /* offset 28h */
+ u32 addr_reg2; /* offset 2Ch */
+ u32 dma_mst_ctrl; /* offset 30h */
+ u32 dma_cfg_a; /* offset 34h */
+ u32 dma_cfg_b; /* offset 38h */
+ u32 fifo_ctrl; /* offset 3Ch */
+ u32 data_block_ptr; /* offset 40h */
+ u32 tag_ptr; /* offset 44h */
+ u32 resv1; /* offset 48h */
+ u32 dec_status; /* offset 4Ch */
+ u32 hwstatus_cmd; /* offset 50h */
+ u32 hwstatus_mask; /* offset 54h */
+ u32 resv2[29];
+ u32 bch_config; /* offset CCh */
+ u32 bch_dec_result; /* offset D0h */
+ u32 bch_dec_status_buf;
+ /* offset D4h */
+};
diff --git a/board/toradex/common/ulpi_linux.c b/board/toradex/common/ulpi_linux.c
new file mode 100644
index 0000000000..b3f617152a
--- /dev/null
+++ b/board/toradex/common/ulpi_linux.c
@@ -0,0 +1,194 @@
+/*
+ * ulpi_linux.c
+ *
+ * this file is ulpi_phy_power_on() function taken from
+ * arch/arm/mach-tegra/usb_phy.c linux kernel code, slightly
+ * modified for U-Boot by Ant Micro <www.antmicro.com>
+ *
+ * Original arch/arm/mach-tegra/usb_phy.c Copyrights:
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 - 2011 NVIDIA Corporation
+ * Erik Gilling <konkers@google.com>
+ * Benoit Goby <benoit@android.com>
+ *
+ * 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>
+#include <asm/io.h>
+
+#define ULPI_VIEWPORT 0x170
+#define ULPI_WAKEUP (1 << 31)
+#define ULPI_RUN (1 << 30)
+#define ULPI_RD_RW_WRITE (1 << 29)
+#define ULPI_RD_RW_READ (0 << 29)
+#define ULPI_PORT(x) (((x) & 0x7) << 24)
+#define ULPI_ADDR(x) (((x) & 0xff) << 16)
+#define ULPI_DATA_RD(x) (((x) & 0xff) << 8)
+#define ULPI_DATA_WR(x) (((x) & 0xff) << 0)
+
+#define USB_PORTSC1 0x184
+#define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
+#define USB_PORTSC1_PSPD(x) (((x) & 0x3) << 26)
+#define USB_PORTSC1_PHCD (1 << 23)
+#define USB_PORTSC1_WKOC (1 << 22)
+#define USB_PORTSC1_WKDS (1 << 21)
+#define USB_PORTSC1_WKCN (1 << 20)
+#define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16)
+#define USB_PORTSC1_PP (1 << 12)
+#define USB_PORTSC1_SUSP (1 << 7)
+#define USB_PORTSC1_PE (1 << 2)
+#define USB_PORTSC1_CCS (1 << 0)
+
+#define USB_SUSP_CTRL 0x400
+#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
+#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
+#define USB_SUSP_CLR (1 << 5)
+#define USB_PHY_CLK_VALID (1 << 7)
+#define UTMIP_RESET (1 << 11)
+#define UHSIC_RESET (1 << 11)
+#define UTMIP_PHY_ENABLE (1 << 12)
+#define ULPI_PHY_ENABLE (1 << 13)
+#define USB_SUSP_SET (1 << 14)
+#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
+
+#define ULPI_TIMING_CTRL_0 0x424
+#define ULPI_OUTPUT_PINMUX_BYP (1 << 10)
+#define ULPI_CLKOUT_PINMUX_BYP (1 << 11)
+
+#define ULPI_TIMING_CTRL_1 0x428
+#define ULPI_DATA_TRIMMER_LOAD (1 << 0)
+#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1)
+#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16)
+#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17)
+#define ULPI_DIR_TRIMMER_LOAD (1 << 24)
+#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25)
+
+#define uint32_t unsigned int
+#define USBADDR2 0xc5004000
+
+#define CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0 0x10
+#define CLK_RST_CONTROLLER_RST_DEVICES_L_0 0x4
+
+static int wait_for_register(uint32_t addr, uint32_t mask, uint32_t result, int timeout_)
+{
+ int timeout = timeout_;
+ while (timeout-- > 0) {
+ if ((readl(addr) & mask) == result) return 0;
+ udelay(2);
+ }
+ return -1;
+}
+
+/* ulpi phy power on */
+void ulpi_phy_power_on(void)
+{
+ uint32_t val;
+ uint32_t base = USBADDR2;
+ uint32_t RegVal = 0;
+
+ udelay(1000);
+
+ /* begin USB reset */
+
+ /* enable USB clock */
+ RegVal = readl(NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0 + 4);
+ RegVal |= (1 << 26);
+// writel(NV_PA_CLK_RST_BASE+CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0+4, RegVal);
+//above not working!
+*((uint *) (NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0 + 4)) = RegVal;
+
+ /* reset USB */
+ RegVal = readl(NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_RST_DEVICES_L_0 + 4);
+ if (RegVal & (1 << 26)) {
+// writel(NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_RST_DEVICES_L_0 + 4, RegVal);
+*((uint *) (NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_RST_DEVICES_L_0 + 4)) = RegVal;
+ udelay(2);
+ RegVal = readl(NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_RST_DEVICES_L_0 + 4);
+ RegVal &= ~(1 << 26);
+// writel(NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_RST_DEVICES_L_0 + 4, RegVal);
+*((uint *) (NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_RST_DEVICES_L_0 + 4)) = RegVal;
+ udelay(2);
+ }
+
+ /* set UTMIP_RESET/UHSIC_RESET */
+ RegVal = readl(base + USB_SUSP_CTRL);
+ RegVal |= (1 << 11);
+ writel(base + USB_SUSP_CTRL, RegVal);
+
+ /* end USB reset */
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= ULPI_PHY_ENABLE;
+ writel(val, base + USB_SUSP_CTRL);
+
+ val = 0;
+ writel(val, base + ULPI_TIMING_CTRL_1);
+
+ val |= ULPI_DATA_TRIMMER_SEL(4);
+ val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
+ val |= ULPI_DIR_TRIMMER_SEL(4);
+ writel(val, base + ULPI_TIMING_CTRL_1);
+
+ udelay(10);
+
+ val |= ULPI_DATA_TRIMMER_LOAD;
+ val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
+ val |= ULPI_DIR_TRIMMER_LOAD;
+ writel(val, base + ULPI_TIMING_CTRL_1);
+
+ val = ULPI_WAKEUP | ULPI_RD_RW_WRITE | ULPI_PORT(0);
+ writel(val, base + ULPI_VIEWPORT);
+
+ if (wait_for_register(base + ULPI_VIEWPORT, ULPI_WAKEUP, 0, 1000)) {
+ printf("%s: timeout waiting for ulpi phy wakeup\n", __func__);
+ return;
+ }
+
+ /* Fix VbusInvalid due to floating VBUS */
+ val = ULPI_RUN | ULPI_RD_RW_WRITE | ULPI_PORT(0) | ULPI_ADDR(0x08) | ULPI_DATA_WR(0x40);
+ writel(val, base + ULPI_VIEWPORT);
+ if (wait_for_register(base + ULPI_VIEWPORT, ULPI_RUN, 0, 1000)) {
+ printf("%s: timeout accessing ulpi phy\n", __func__);
+ return;
+ }
+ val = ULPI_RUN | ULPI_RD_RW_WRITE | ULPI_PORT(0) | ULPI_ADDR(0x0B) | ULPI_DATA_WR(0x80);
+ writel(val, base + ULPI_VIEWPORT);
+ if (wait_for_register(base + ULPI_VIEWPORT, ULPI_RUN, 0, 1000)) {
+ printf("%s: timeout accessing ulpi phy\n", __func__);
+ return;
+ }
+
+ val = readl(base + USB_PORTSC1);
+ val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN;
+ writel(val, base + USB_PORTSC1);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+ udelay(100);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+}
diff --git a/board/toradex/common/usb.c b/board/toradex/common/usb.c
new file mode 100644
index 0000000000..63accf8394
--- /dev/null
+++ b/board/toradex/common/usb.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * (C) Copyright 2010,2011 NVIDIA Corporation <www.nvidia.com>
+ * (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>
+#include <asm/io.h>
+#include <asm/arch-tegra/bitfield.h>
+#include <asm/arch/tegra.h>
+#include <asm/arch/sys_proto.h>
+
+#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch-tegra/uart.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/usb.h>
+#include <fdt_decode.h>
+
+#define CLK_RST_CONTROLLER_CLK_OUT_ENB_U_0 0x18
+#define CLK_RST_CONTROLLER_PLLP_OUTB_0 0xA8
+
+enum {
+ USB_PORTS_MAX = 4, /* Maximum ports we allow */
+};
+
+struct usb_port {
+ struct usb_ctlr *reg;
+};
+
+static struct usb_port port[USB_PORTS_MAX]; /* List of valid USB ports */
+static unsigned port_count; /* Number of available ports */
+
+/* Record which controller can switch from host to device mode */
+static struct usb_ctlr *host_dev_ctlr;
+
+/*
+ * This table has USB timing parameters for each Oscillator frequency we
+ * support. There are four sets of values:
+ *
+ * 1. PLLU configuration information (reference clock is osc/clk_m and
+ * PLLU-FOs are fixed at 12MHz/60MHz/480MHz).
+ * (T2x)
+ * Reference frequency 13.0MHz 19.2MHz 12.0MHz 26.0MHz
+ * ----------------------------------------------------------------------
+ * DIVN 960 (0x3c0) 200 (0c8) 960 (3c0h) 960 (3c0)
+ * DIVM 13 (0d) 4 (04) 12 (0c) 26 (1a)
+ * Filter frequency (MHz) 1 4.8 6 2
+ * CPCON 1100b 0011b 1100b 1100b
+ * LFCON0 0 0 0 0
+ *
+ * (T3x)
+ * Reference frequency MHZ 12.0 13.0 16.8 19.2 26.0 38.4 48.0
+ * ----------------------------------------------------------------------
+ * DIVN 960 960 400 200 960 200 960
+ * DIVM 12 13 7 4 26 4 12
+ *
+ * 2. PLL CONFIGURATION & PARAMETERS for different clock generators:
+ * (T2x)
+ * Reference frequency 13.0MHz 19.2MHz 12.0MHz 26.0MHz
+ * ---------------------------------------------------------------------------
+ * Index 0 1 2 3
+ * PLLU_ENABLE_DLY_COUNT 02 (0x02) 03 (03) 02 (02) 04 (04)
+ * PLLU_STABLE_COUNT 51 (33) 75 (4B) 47 (2F) 102 (66)
+ * PLL_ACTIVE_DLY_COUNT 05 (05) 06 (06) 04 (04) 09 (09)
+ * XTAL_FREQ_COUNT 127 (7F) 187 (BB) 118 (76) 254 (FE)
+ *
+ * (T3x)
+ * Reference frequency MHZ 12.0 13.0 16.8 19.2 26.0 38.4 48.0
+ * ---------------------------------------------------------------------------
+ * Index 8 0 1 4 12 5 9
+ * PLLU_ENABLE_DLY_COUNT 02 2 3 3 4 5 6
+ * PLLU_STABLE_COUNT 47 51 66 75 102 150 188
+ * PLL_ACTIVE_DLY_COUNT 08 9 11 12 17 24 31
+ * XTAL_FREQ_COUNT 118 127 165 188 254 375 469
+ *
+ * 3. Debounce values IdDig, Avalid, Bvalid, VbusValid, VbusWakeUp, and
+ * SessEnd. Each of these signals have their own debouncer and for each of
+ * those one out of two debouncing times can be chosen (BIAS_DEBOUNCE_A or
+ * BIAS_DEBOUNCE_B).
+ *
+ * The values of DEBOUNCE_A and DEBOUNCE_B are calculated as follows:
+ * 0xffff -> No debouncing at all
+ * <n> ms = <n> *1000 / (1/19.2MHz) / 4
+ *
+ * So to program a 1 ms debounce for BIAS_DEBOUNCE_A, we have:
+ * BIAS_DEBOUNCE_A[15:0] = 1000 * 19.2 / 4 = 4800 = 0x12c0
+ *
+ * We need to use only DebounceA for BOOTROM. We don’t need the DebounceB
+ * values, so we can keep those to default.
+ *
+ * 4. The 20 microsecond delay after bias cell operation.
+ */
+#if !defined(CONFIG_TEGRA3)
+static const int usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = {
+ /* DivN, DivM, DivP, CPCON, LFCON,EN_DLY,STB,ACT, XTAL,Debounce,Bias */
+ { 0x3C0, 0x0D, 0x00, 0xC, 0, 0x02, 0x33, 0x05, 0x7F, 0x7EF4, 5 },
+ { 0x0C8, 0x04, 0x00, 0x3, 0, 0x03, 0x4B, 0x06, 0xBB, 0xBB80, 7 },
+ { 0x3C0, 0x0C, 0x00, 0xC, 0, 0x02, 0x2F, 0x04, 0x76, 0x7530, 5 },
+ { 0x3C0, 0x1A, 0x00, 0xC, 0, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 }
+};
+#endif
+/* UTMIP Idle Wait Delay */
+static const u8 utmip_idle_wait_delay = 17;
+
+/* UTMIP Elastic limit */
+static const u8 utmip_elastic_limit = 16;
+
+/* UTMIP High Speed Sync Start Delay */
+static const u8 utmip_hs_sync_start_delay = 9;
+
+extern void ulpi_phy_power_on(void);
+
+/* Put the port into host mode (this only works for USB1) */
+static void set_host_mode(struct usb_ctlr *usbctlr)
+{
+//check USB3
+ /* Check whether remote host from USB1 is driving VBus */
+ if (bf_readl(VBUS_VLD_STS, &usbctlr->phy_vbus_sensors))
+ return;
+
+#if !defined(CONFIG_TEGRA3)
+//only required for USB3 at least on Iris
+ /*
+ * If not driving, we set GPIO USB1_VBus_En. Colibri T20 uses
+ * PAD SPIG (GPIO W.02) as USB1_VBus_En Config as GPIO
+ */
+ gpio_direction_output(GPIO_PW2, 0);
+
+ /* Z_SPIG = 0, normal, not tristate */
+ pinmux_tristate_disable(PINGRP_SPIG);
+#else
+ /* T30 pinmuxes are set globally; GPIOs from fdt */
+#endif
+}
+
+/* Put our ports into host mode */
+void usb_set_host_mode(void)
+{
+ if (host_dev_ctlr)
+ set_host_mode(host_dev_ctlr);
+}
+
+void usbf_reset_controller(enum periph_id id, struct usb_ctlr *usbctlr)
+{
+#if !defined(CONFIG_TEGRA3)
+ uint reg;
+
+ /* Configure ULPI pin mux */
+//ToDo: find better place to do this
+ pinmux_set_func(PINGRP_UAA, PMUX_FUNC_ULPI);
+ pinmux_set_func(PINGRP_UAB, PMUX_FUNC_ULPI);
+ pinmux_set_func(PINGRP_UDA, PMUX_FUNC_ULPI);
+ pinmux_tristate_disable(PINGRP_UAA);
+ pinmux_tristate_disable(PINGRP_UAB);
+ pinmux_tristate_disable(PINGRP_UDA);
+#endif
+
+ /* Reset the USB controller with 2us delay */
+ reset_periph(id, 2);
+
+#if !defined(CONFIG_TEGRA3)
+ if (id == PERIPH_ID_USB2) {
+
+ /* Reset ULPI PHY */
+ gpio_direction_output(GPIO_PV1, 0);
+ pinmux_tristate_disable(PINGRP_UAC);
+ udelay(5000);
+ gpio_set_value(GPIO_PV1, 1);
+
+ /* Configure CDEV2 as PLLP_OUT4 */
+ pinmux_set_func(PINGRP_CDEV2, PMUX_FUNC_PLLP_OUT4);
+ pinmux_tristate_disable(PINGRP_CDEV2);
+
+ /* Configure 24 MHz clock for ULPI PHY */
+//enable
+ reg = readl(NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_CLK_OUT_ENB_U_0);
+ reg |= (1 << 29);
+// writel(NV_PA_CLK_RST_BASE+CLK_RST_CONTROLLER_CLK_OUT_ENB_U_0, reg);
+//above not working!
+*((uint *) (NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_CLK_OUT_ENB_U_0)) = reg;
+//rate
+ reg = readl(NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_PLLP_OUTB_0);
+ reg |= (8 << 25) | (0 << 24) | (1 << 18);
+// writel(NV_PA_CLK_RST_BASE+CLK_RST_CONTROLLER_PLLP_OUTB_0, reg);
+*((uint *) (NV_PA_CLK_RST_BASE + CLK_RST_CONTROLLER_PLLP_OUTB_0)) = reg;
+
+ ulpi_phy_power_on();
+
+ /* Fix Ethernet detection faults */
+ udelay(100 * 1000);
+
+ /* Enable ASIX AX88772B V_BUS */
+ gpio_direction_output(GPIO_PBB1, 1);
+ pinmux_tristate_disable(PINGRP_DTE);
+ }
+
+ /*
+ * Set USB1_NO_LEGACY_MODE to 1, Registers are accessible under
+ * base address
+ */
+//what about tegra 3?
+ if (id == PERIPH_ID_USBD)
+ bf_writel(USB1_NO_LEGACY_MODE, NO_LEGACY_MODE,
+ &usbctlr->usb1_legacy_ctrl);
+
+ /* Put UTMIP1/3 in reset */
+ if ((id == PERIPH_ID_USBD) || (id == PERIPH_ID_USB3))
+ bf_writel(UTMIP_RESET, 1, &usbctlr->susp_ctrl);
+
+ /* Set USB3 to use UTMIP PHY */
+ if (id == PERIPH_ID_USB3)
+ bf_writel(UTMIP_PHY_ENB, 1, &usbctlr->susp_ctrl);
+#else /* !CONFIG_TEGRA3 */
+ /* Put UTMIP1/2/3 in reset */
+ bf_writel(UTMIP_RESET, 1, &usbctlr->susp_ctrl);
+
+ /* Set USB1/2/3 to use UTMIP PHY */
+ bf_writel(UTMIP_PHY_ENB, 1, &usbctlr->susp_ctrl);
+#endif
+
+ /*
+ * TODO: where do we take the USB1 out of reset? The old code would
+ * take USB3 out of reset, but not USB1. This code doesn't do either.
+ */
+}
+
+/* set up the USB controller with the parameters provided */
+static void init_usb_controller(enum periph_id id, struct usb_ctlr *usbctlr,
+ const int *params)
+{
+ u32 val;
+ int loop_count;
+#if defined(CONFIG_TEGRA3)
+ struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+#endif
+ clock_enable(id);
+
+ /* Reset the usb controller */
+ usbf_reset_controller(id, usbctlr);
+
+ /* Stop crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN low */
+ bf_clearl(UTMIP_PHY_XTAL_CLOCKEN, &usbctlr->utmip_misc_cfg1);
+
+ /* Follow the crystal clock disable by >100ns delay */
+ udelay(1);
+#if !defined(CONFIG_TEGRA3)
+ /*
+ * To Use the A Session Valid for cable detection logic, VBUS_WAKEUP
+ * mux must be switched to actually use a_sess_vld threshold.
+ */
+ if (id == PERIPH_ID_USBD)
+ bf_enum_writel(VBUS_SENSE_CTL, A_SESS_VLD,
+ &usbctlr->usb1_legacy_ctrl);
+
+ /*
+ * PLL Delay CONFIGURATION settings. The following parameters control
+ * the bring up of the plls.
+ */
+ val = readl(&usbctlr->utmip_misc_cfg1);
+ bf_update(UTMIP_PLLU_STABLE_COUNT, val, params[PARAM_STABLE_COUNT]);
+ bf_update(UTMIP_PLL_ACTIVE_DLY_COUNT, val,
+ params[PARAM_ACTIVE_DELAY_COUNT]);
+ writel(val, &usbctlr->utmip_misc_cfg1);
+
+ /* Set PLL enable delay count and crystal frequency count */
+ val = readl(&usbctlr->utmip_pll_cfg1);
+ bf_update(UTMIP_PLLU_ENABLE_DLY_COUNT, val,
+ params[PARAM_ENABLE_DELAY_COUNT]);
+ bf_update(UTMIP_XTAL_FREQ_COUNT, val, params[PARAM_XTAL_FREQ_COUNT]);
+ writel(val, &usbctlr->utmip_pll_cfg1);
+#else /* !CONFIG_TEGRA3 */
+ /*
+ * PLL Delay CONFIGURATION settings. The following parameters control
+ * the bring up of the plls.
+ */
+ val = readl(&clkrst->crc_pll_cfg2);
+ bf_update(UTMIP_PLLU_STABLE_COUNT, val, params[PARAM_STABLE_COUNT]);
+ bf_update(UTMIP_PLL_ACTIVE_DLY_COUNT, val,
+ params[PARAM_ACTIVE_DELAY_COUNT]);
+ writel(val, &clkrst->crc_pll_cfg2);
+
+ /* Set PLL enable delay count and crystal frequency count */
+ val = readl(&clkrst->crc_pll_cfg1);
+ bf_update(UTMIP_PLLU_ENABLE_DLY_COUNT, val,
+ params[PARAM_ENABLE_DELAY_COUNT]);
+ bf_update(UTMIP_XTAL_FREQ_COUNT, val, params[PARAM_XTAL_FREQ_COUNT]);
+ writel(val, &clkrst->crc_pll_cfg1);
+
+ /* Disable Power Down state for PLL */
+ bf_writel(UTMIP_FORCE_PLLU_POWERDOWN, 0, &clkrst->crc_pll_cfg1);
+ bf_writel(UTMIP_FORCE_PLL_ENABLE_POWERDOWN, 0, &clkrst->crc_pll_cfg1);
+ bf_writel(UTMIP_FORCE_PLL_ACTIVE_POWERDOWN, 0, &clkrst->crc_pll_cfg1);
+
+ /* Recommended PHY settings for EYE diagram */
+ bf_writel(UTMIP_XCVR_SETUP, 0x4, &usbctlr->utmip_xcvr_cfg0);
+ bf_writel(UTMIP_XCVR_SETUP_MSB, 0x3, &usbctlr->utmip_xcvr_cfg0);
+ bf_writel(UTMIP_XCVR_HSSLEW_MSB, 0x8, &usbctlr->utmip_xcvr_cfg0);
+ bf_writel(UTMIP_XCVR_TERM_RANGE_ADJ, 0x7, &usbctlr->utmip_xcvr_cfg1);
+ bf_writel(UTMIP_HSDISCON_LEVEL_MSB, 0x1, &usbctlr->utmip_bias_cfg0);
+ bf_writel(UTMIP_HSDISCON_LEVEL, 0x1, &usbctlr->utmip_bias_cfg0);
+ bf_writel(UTMIP_HSSQUELCH_LEVEL, 0x2, &usbctlr->utmip_bias_cfg0);
+
+ /* Miscellaneous setting mentioned in Programming Guide */
+ bf_writel(UTMIP_SUSPEND_EXIT_ON_EDGE, 0, &usbctlr->utmip_misc_cfg0);
+#endif /* !CONFIG_TEGRA3 */
+ /* Setting the tracking length time */
+ bf_writel(UTMIP_BIAS_PDTRK_COUNT, params[PARAM_BIAS_TIME],
+ &usbctlr->utmip_bias_cfg1);
+
+ /* Program debounce time for VBUS to become valid */
+ bf_writel(UTMIP_DEBOUNCE_CFG0, params[PARAM_DEBOUNCE_A_TIME],
+ &usbctlr->utmip_debounce_cfg0);
+
+ /* Set UTMIP_FS_PREAMBLE_J to 1 */
+ bf_writel(UTMIP_FS_PREAMBLE_J, 1, &usbctlr->utmip_tx_cfg0);
+
+ /* Disable battery charge enabling bit */
+ bf_writel(UTMIP_PD_CHRG, 1, &usbctlr->utmip_bat_chrg_cfg0);
+
+ /* Set UTMIP_XCVR_LSBIAS_SEL to 0 */
+ bf_writel(UTMIP_XCVR_LSBIAS_SE, 0, &usbctlr->utmip_xcvr_cfg0);
+
+ /* Set bit 3 of UTMIP_SPARE_CFG0 to 1 */
+ bf_writel(FUSE_SETUP_SEL, 1, &usbctlr->utmip_spare_cfg0);
+
+ /*
+ * Configure the UTMIP_IDLE_WAIT and UTMIP_ELASTIC_LIMIT
+ * Setting these fields, together with default values of the
+ * other fields, results in programming the registers below as
+ * follows:
+ * UTMIP_HSRX_CFG0 = 0x9168c000
+ * UTMIP_HSRX_CFG1 = 0x13
+ */
+
+ /* Set PLL enable delay count and Crystal frequency count */
+ val = readl(&usbctlr->utmip_hsrx_cfg0);
+ bf_update(UTMIP_IDLE_WAIT, val, utmip_idle_wait_delay);
+ bf_update(UTMIP_ELASTIC_LIMIT, val, utmip_elastic_limit);
+ writel(val, &usbctlr->utmip_hsrx_cfg0);
+
+ /* Configure the UTMIP_HS_SYNC_START_DLY */
+ bf_writel(UTMIP_HS_SYNC_START_DLY, utmip_hs_sync_start_delay,
+ &usbctlr->utmip_hsrx_cfg1);
+
+ /* Precede the crystal clock enable by >100ns delay. */
+ udelay(1);
+
+ /* Resuscitate crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN */
+ bf_writel(UTMIP_PHY_XTAL_CLOCKEN, 1, &usbctlr->utmip_misc_cfg1);
+#if defined(CONFIG_TEGRA3)
+ if (id == PERIPH_ID_USBD)
+ bf_writel(UTMIP_FORCE_PD_SAMP_A_POWERDOWN, 0,
+ &clkrst->crc_pll_cfg2);
+ if (id == PERIPH_ID_USB2)
+ bf_writel(UTMIP_FORCE_PD_SAMP_B_POWERDOWN, 0,
+ &clkrst->crc_pll_cfg2);
+ if (id == PERIPH_ID_USB3)
+ bf_writel(UTMIP_FORCE_PD_SAMP_C_POWERDOWN, 0,
+ &clkrst->crc_pll_cfg2);
+#endif
+ /* Finished the per-controller init. */
+
+ /* De-assert UTMIP_RESET to bring out of reset. */
+ bf_clearl(UTMIP_RESET, &usbctlr->susp_ctrl);
+
+ /* Wait for the phy clock to become valid in 100 ms */
+ for (loop_count = 100000; loop_count != 0; loop_count--) {
+ if (bf_readl(USB_PHY_CLK_VALID, &usbctlr->susp_ctrl))
+ break;
+ udelay(1);
+ }
+}
+
+static void power_up_port(struct usb_ctlr *usbctlr)
+{
+ u32 val;
+
+ /* Deassert power down state */
+ val = readl(&usbctlr->utmip_xcvr_cfg0);
+ bf_update(UTMIP_FORCE_PD_POWERDOWN, val, 0);
+ bf_update(UTMIP_FORCE_PD2_POWERDOWN, val, 0);
+ bf_update(UTMIP_FORCE_PDZI_POWERDOWN, val, 0);
+ writel(val, &usbctlr->utmip_xcvr_cfg0);
+
+ val = readl(&usbctlr->utmip_xcvr_cfg1);
+ bf_update(UTMIP_FORCE_PDDISC_POWERDOWN, val, 0);
+ bf_update(UTMIP_FORCE_PDCHRP_POWERDOWN, val, 0);
+ bf_update(UTMIP_FORCE_PDDR_POWERDOWN, val, 0);
+ writel(val, &usbctlr->utmip_xcvr_cfg1);
+}
+
+static void config_clock(const int params[])
+{
+ clock_start_pll(CLOCK_ID_USB,
+ params[PARAM_DIVM], params[PARAM_DIVN],
+ params[PARAM_DIVP], params[PARAM_CPCON],
+ params[PARAM_LFCON]);
+}
+
+/**
+ * Add a new USB port to the list of available ports
+ *
+ * @param id peripheral id of port (PERIPH_ID_USB3, for example)
+ * @param usbctlr register address of controller
+ * @param params timing parameters
+ * @param utmi 1 if using internal UTMI transceiver
+ * @return 0 if ok, -1 if error (too many ports)
+ */
+static int add_port(enum periph_id id, struct usb_ctlr *usbctlr,
+ const int params[], int utmi)
+{
+ volatile u32 *ahb_prefetch_reg;
+
+ if (port_count == USB_PORTS_MAX) {
+ debug("tegrausb: Cannot register more than %d ports\n",
+ USB_PORTS_MAX);
+ return -1;
+ }
+ init_usb_controller(id, usbctlr, params);
+#if defined(CONFIG_TEGRA3)
+ /*
+ * BIAS Pad Power Down is common among all 3 USB
+ * controllers and can be controlled from USB1 only.
+ */
+ if (id == PERIPH_ID_USBD)
+ bf_writel(UTMIP_BIASPD, 0, &usbctlr->utmip_bias_cfg0);
+#endif
+ if (utmi) {
+ /* Disable ICUSB FS/LS transceiver */
+//should actually default to disabled on Tegra 3 according to TRM
+ bf_writel(IC_ENB1, 0, &usbctlr->icusb_ctrl);
+
+#if !defined(CONFIG_TEGRA3)
+ /* Select UTMI parallel interface */
+ bf_writel(PTS, PTS_UTMI, &usbctlr->port_sc1);
+ bf_writel(STS, 0, &usbctlr->port_sc1);
+#endif
+//probably just following required
+ power_up_port(usbctlr);
+ }
+ port[port_count++].reg = usbctlr;
+
+ /* Setting AHB-Prefetch register to avoid TX FIFO underrun
+ Colibri T20 has Ethernet on USB2 */
+ if (id == PERIPH_ID_USB2) {
+// printf("setting AHB-prefetch registers for USB2\n");
+ ahb_prefetch_reg = (u32 *)0x6000c0f4;
+ /* AHB_AHB_MEM_PREFETCH_CFG2_0
+ 0b1: ENABLE
+ 0b10010: 18 = USB2
+ 0b01100: ADDR_BNDRY 2^(12+4) = 65536
+ 0x800: INACTIVITY_TIMEOUT */
+ *ahb_prefetch_reg = 0xc9800800;
+ }
+
+ return 0;
+}
+
+int tegrausb_start_port(unsigned portnum, struct ehci_hccr **hccr,
+ struct ehci_hcor **hcor)
+{
+ struct usb_ctlr *usbctlr;
+
+ if (portnum >= port_count)
+ return -1;
+ tegrausb_stop_port(portnum);
+
+ usbctlr = port[portnum].reg;
+#if defined(CONFIG_TEGRA3)
+ /* Set Controller Mode as Host mode after Controller Reset was done */
+ bf_writel(CM, CM_HOST_MODE, &usbctlr->usb_mode);
+
+ /* Select UTMI parallel interface after setting host mode */
+ bf_writel(PTS, PTS_UTMI, &usbctlr->hostpc1_devlc);
+ bf_writel(STS, STS_PARALLEL_IF, &usbctlr->hostpc1_devlc);
+#endif
+
+ *hccr = (struct ehci_hccr *)&usbctlr->cap_length;
+ *hcor = (struct ehci_hcor *)&usbctlr->usb_cmd;
+ return 0;
+}
+
+int tegrausb_stop_port(unsigned portnum)
+{
+ struct usb_ctlr *usbctlr;
+
+ if (portnum >= port_count)
+ return -1;
+
+ usbctlr = port[portnum].reg;
+
+ /* Stop controller */
+ writel(0, &usbctlr->usb_cmd);
+ udelay(1000);
+
+ /* Initiate controller reset */
+ writel(2, &usbctlr->usb_cmd);
+ udelay(1000);
+ return 0;
+}
+
+int board_usb_init(const void *blob)
+{
+ struct fdt_usb config;
+ int clk_done = 0;
+ int node, upto = 0;
+ unsigned osc_freq = clock_get_rate(CLOCK_ID_OSC);
+
+ do {
+ node = fdt_decode_next_alias(blob, "usb",
+ COMPAT_NVIDIA_TEGRA250_USB, &upto);
+ if (node < 0)
+ break;
+ if (fdt_decode_usb(blob, node, osc_freq, &config))
+ return -1;
+ if (!config.enabled)
+ continue;
+
+ /* The first port we find gets to set the clocks */
+ if (!clk_done) {
+ config_clock(config.params);
+ clk_done = 1;
+ }
+ if (config.host_mode) {
+ /* Only one host-dev port is supported */
+ if (host_dev_ctlr)
+ return -1;
+ host_dev_ctlr = config.reg;
+ }
+ if (add_port(config.periph_id, config.reg, config.params,
+ config.utmi))
+ return -1;
+
+ fdt_setup_gpio(&config.vbus_gpio);
+ fdt_setup_gpio(&config.vbus_pullup_gpio);
+ } while (node);
+
+ usb_set_host_mode();
+
+ return 0;
+}